I would like a function I can call this way:
my_printf(int unimportant,
"%-10s", "a string",
"%5.1f", 1.23,
format_string, value_to_format,
/* possibly more pairs like this */
(char *) NULL);
We may assume that the format strings contain at most one % escape sequence each.
I would like to use one of the sprintf family to format the values, perhaps producing a result like "a string , 1.2, …".
So far I know several ways that do not work. The following code shows (an incomplete sketch of) the basic idea:
char * my_printf(int uninmportant, ...)
{
va_arg ap;
va_start(ap, unimportant);
while (1) {
char *format = va_arg(ap, char *);
if (!format) break;
vsprintf(buffer, format, ap);
/* append buffer somewhere */
}
}
This doesn't work because there is no guarantee about the usefulness of ap on return from vsprintf. It could be pointing anywhere or nowhere. The usual fix for that is to copy ap with va_copy before passing it. But that doesn't help in this case:
char * my_printf(int uninmportant, ...)
{
va_arg ap;
va_start(ap, unimportant);
while (1) {
char *format = va_arg(ap, char *);
if (!format) break;
va_arg ap_copy;
va_copy(ap_copy, ap);
vsprintf(buffer, format, ap_copy);
/* append buffer somewhere */
/* ?? seek ap forward to point to the next format string ?? */
}
}
Now vsprintf no longer destroys ap. But "seek ap forward to point to the next format string" seems quite difficult, because it seems to involve parsing and interpreting the contents of format, at least sufficiently well to know what type to pass to va_arg.
Similarly if I try to get around the problem by using sprintf, I still have to parse the format string in order to uunderstand how to call sprintf:
char * my_printf(int uninmportant, ...)
{
va_arg ap;
va_start(ap, unimportant);
while (1) {
char *format = va_arg(ap, char *);
if (!format) break;
??TYPE?? arg = va_arg(ap, ??TYPE??);
sprintf(buffer, format, arg);
/* append buffer somewhere */
}
}
Again it seems the only way around this is to parse the value of format and then have a big switch with an arm for each possible type.
I can't have been the first or the thousandth person to want to do this. What is the conventional wisdom here?
- Here is what you have overlooked: …
- You are out of luck, it is well-known that there is no portable solution
- The only way to do this is to parse the format strings
- There is a dirty trick you can play with the preprocessor as follows: …
- The following code, published in 1982 in the Bell System Technical Journal, is helpful, and everyone uses some variation of it: …
- Your
my_printfcalling syntax is misconceived. Have it be called this other way instead: … - (something else?)
I am aware that some compilers, such as GCC, provide nonportable extensions to assist with this, such as parse_printf_format, but I would like a more portable solution.
Thanks for any suggestions.
apis indeterminate after passing it tovsprintf, and the C standard does not provide any facility that would tell you how many arguments were consumed in performing thevsprintfother than examining the format yourself. You cannot even write a preprocessing program to transformmy_printfcalls in some way that supplies the needed information because it would be faced with the same problem, counting the arguments to be consumed, so it has to examine the format strings too,…va_end()before leaving the function. You also need to callva_end()after you passaptovsprintf()becauseapis in an indeterminate state on return fromvsprintf(). And, therefore, you need to callva_start()again. Each call tova_start()orva_copy()needs a correspondingva_end()in the function that invokesva_start()orva_copy().int unimportantis not needed.