Compile-time validation of format strings in various forms is one of the most
requested features in the fmt project on GitHub
#546) and a major request
from the C++ standards committee after the review of my formatting paper.
In 2015 I came up with an unsatisfactory solution
to this problem that only worked with printf syntax and involved macros.
But inspired by recent progress in
constexpr support in modern compilers and the
work of various people, most importantly, Michael Park’s cool
as well as Ben Deane & Jason Turner’s illuminating talk
constexpr ALL the Things!”,
I decided to give it another go.
After some struggling with compilers and their various degrees of
constexpr-readiness, I am finally happy to announce that the
std branch of the fmt library now
implements compile-time format string checks!
First, let’s take a look at a simple example (
and try compiling it:
The first part of the error about initialization of
invalid_format variable is
pretty generic and not very useful, but then we get the exact cause of the
because we passed 2 as the argument index while provided only 1 argument. Also we get the error location:
So we have all we need to quickly navigate to the problematic line and fix it:
OK, this is nice but we’ve seen it before. What’s so special about the new implementation?
First, the format string parsing code is the same between runtime and
compile time. The code is super readable and doesn’t have any recursive
templates or value-encoded types that were often needed to workaround lack of
constexpr function parameters:
Second, full Python-esque format string syntax is supported and not just some basic subset of it. Argument types are taken into account as well, for example
will give you an error because precision (
.2) is not allowed for integral
types. At the same time
will pass the checks because
4.2 is a floating-point number and can be
formatted with precision.
And last, but not least, this functionality is available for user-defined types,
so if a user-provided format spec parsing function is
checks will Just Work (tm) for it as well:
To the best of my knowledge fmt is the first C++ formatting library with this capability (but correct me if I’m wrong in the comments below). I’ll cover support for user-defined types in more details in a separate post since it’s a big topic on its own.
This functionality is still experimental and, although parsing is fully implemented, some errors are not reported yet which will be addressed shortly. The compile-time checks work on GCC and Clang only, because they require user-defined literal templates which is a GCC extension. The code is compatible with Visual C++ but gracefully degrades with compile-time checks replaced with runtime ones.
This is not the end though. Compile-time parsing of format strings can be used not just for checks but, as pointed out by Louis Dionne, the author of the famous Boost.Hana library, also to generate optimal formatting code. This will make the separate write API unnecessary and make fmt even easier to use.
Thanks to the C++ Library Evolution Working Group members for encouraging me working on this. Special thanks to Zach Laine for the initial feedback and providing a code example of constexpr compile-time parsing.