Gotta tell you folks, I’m a C++ coder that uses printf-like functions. Jokes apart, there are some
serious holy wars involving the use of those format functions in C++, functions like std::printf()
,
std::sscanf()
and similar C functions that take a variable amount of parameters.
Sure C++ has its << >>
stream operators that can do the same job in a way nobody can easily read,
but I just think that nothing beats the simplicity and clarity of a printf
:
I mean, there is no way you can’t read that, even if you come from a language that lets you concatenate strings
with the +
operator. Now compare that to an std::cout
:
Actually, that example doesn’t look that bad in the std::cout
version.
Now how about printing a simple hexadecimal number? Things start to get worse for Mister cout
:
The main problem with these variadic or “format” functions, however, is that they make it super easy for you to shoot yourself in the foot.
I’ll always remember a day when a friend asked me for help on how to parse a string read from a file.
I suggested that he used std::sscanf()
. He did, only he was new to these functions (coming from C# to C++)
and tried to pass an std::string
object to the function. The compiler accepted that happily, but of course,
the state of the program was corrupted after the sscanf
call returned. Variadic functions, being inherited from C,
can only deal with native types. They don’t understand the concept of C++ objects and would try to treat them as raw bytes.
There are several replacements for the old C format functions these days, Boost provides a fantastic Formating Library. With C++11, we can now implement safer templated printf-like functions that accept any data type and are type-safe.
But way before Boost and C++11 there was a way to avoid errors like the one I’ve described above.
GCC has the __attribute__((format))
extension. Clang on OSX also fully supports it.
So if you declare your format functions as:
And try do do something silly like passing a C++ string to it, the compiler will pull your years:
Yey! The compiler is doing its job! You will be able to get some sleep tonight.
This also produces a warning if you pass types that are incompatible with the format flag
in the string, such as mismatching int
(%d/%i
) with float|double
(%f/%lf
).
__attribute__((format))
can be used with any type of function, including class methods.
But there is one little detail when using it in a class. A class method has an implicit first parameter,
the this
pointer. So you need to increment the string-index
and first-to-check
of format()
by one:
In fact, this is such a basic feature that even Microsoft has finally added it to
Visual Studio with the SA_FormatString
annotation:
A slightly weird syntax, but does the job.
So if you love variadic function like I do, then do yourself a favor and use these source annotations/attributes and let the compiler do the validation for you (and don’t ignore the warnings when you get them, obviously!).