views:

122

answers:

5

I would like to write a routine like printf, not functionally-wise, but rather I'd like the routine to have the same time compile check characteristics as printf.

For example if i have:

{
   int i;
   std::string s;
   printf("%d %d",i);
   printf("%d",s.c_str());
}

The compiler complains like so:

1 cc1plus: warnings being treated as errors
2 In function 'int main()':
3 Line 8: warning: too few arguments for format
4 Line 9: warning: format '%d' expects type 'int', but argument 2 has type 'const char*'

code example

Are printf and co special functions that the compiler treats differently or is there some trick to getting this to work on any user defined function? The specific compilers I'm interested in are gcc and msvc

+6  A: 

printf() and friends aren't special because they accept a variable number of arguments: user-defined functions can accept a variable number of arguments too. They are special because their behavior is defined by standard, so the compiler knows what the correlation should be between the format string and the arguments passed to the function.

Effectively, the compiler knows how many arguments are passed to the function when it is called, so it parses the format string and compares the expected number and types of arguments against what arguments are actually passed to the function and issues a warning if they don't match.

If you are using C++, I would avoid writing your own variadic functions; there are few good reasons to use them in most projects. For example, if you are doing formatting, use streams or a library like Boost Format. Any problem that can be solved using a variadic function can be solved using a non-variadic function, and in almost all cases the result is more elegant, idiomatic, and type-safe.

James McNellis
There are actually many good reasons to avoid streams and Boost when one is coding in C++, but all of them are domain specific. Most of us do some amount of domain specific work so we should try to not lose track of the fact that there are substantial numbers of people for whom many C++ features are too expensive.
dash-tom-bang
@dash-tom-bang: Yes, you're right: there certainly are scenarios in which they are useful.
James McNellis
+5  A: 

This behavior is highly compiler dependent. I believe that gcc provides an interface for type checking variadic functions.

dmckee
+3  A: 

Actually printf doesn't have any inherent compile-time safety at all. It just happens that some more recent compilers have implemented special checks given that they know exactly what a format string means in terms of the additional parameters. When you use ... as a parameter you're saying that you want to accept arbitrary arguments and accept full responsibility for making sure they're correct. There's no way for the compiler to check them for count/type safety.

Instead of trying to get the compiler to help you in this way, try using the approach used by standard streams: Make use of a (possibly template) function or operator that returns a reference to this to allow chaining. Then the compiler will be able to tell you right away when the arguments don't match what's expected/supported.

Mark B
+3  A: 

A while back someone posted an mpl::string to the boost groups. I think it may actually have gotten into the library. If that is the case you could implement something like this by providing your template string as a template parameter (an mpl::string) and then using some pretty profound meta-programming skills to parse the formatting bits in it. Then you'd use this information to chose an implementation that has the appropriate argument count and types.

No, I'm not going to do it for you :P It would be quite difficult. However, I do believe it would be possible.

Noah Roberts
+8  A: 

Different compilers might implement this functionality differently. In GCC it is implemented through __attribute__ specifier with format attribute (read about it here). The reason why the compiler performs the checking is just that in the standard header files supplied with GCC the printf function is declared with __attribute__((format(printf, 1, 2)))

In exactly the same way you can use format attribute to extend the same format-checking functionality to your own variadic functions that use the same format specifiers as printf.

This all will only work if the parameter passing convention and the format specifiers you use are the same as the ones used by the standard printf and scanf functions. The checks are hardcoded into the compiler. If you are using a different convention for variadic argument passing, the compiler will not help you to check it.

AndreyT
It can do a little more than printf and scanf; the list from the current docs is "printf, scanf, strftime, gnu_printf, gnu_scanf, gnu_strftime or strfmon"
Jefromi
That's cool. I hope CodeGear/Embarcadero puts that feature into their compiler in the future.
Remy Lebeau - TeamB