3

I am currently trying to pass an arbitrary number of parameters of MyType to fprintf like this:

void myPrint(const char* fmt, std::initializer_list<MyType> args)
{
  fprintf(stdout, fmt, args);
}

where MyType is basically a variant and can be constructed from most common types (int, string, uint*, bool, ...). Now when calling myPrint(...) like this

myPrint("I am a hex int: %x", { 5 } );

this will always print the address of the initializer_list. When looking at the references, e.g. cplusplus.com, it says

[...] If format includes format specifiers (subsequences beginning with %), the additional arguments following format are formatted and inserted in the resulting string replacing their respective specifiers. [...]

or microsoft.com

[...] Each function argument (if any) is converted and output according to the corresponding format specification in format. [...]

So, my question is: How does fprintf format or convert its arguments? Does it use operators? Does it use casts?

vonludi
  • 419
  • 2
  • 20
  • 4
    By "converted", Microsoft means "converted into a string according to its format specifier". There is no casting. It's undefined behavior to lie about the type of an argument passed to a variadic function like this. – Cody Gray - on strike Oct 17 '17 at 08:10
  • Can't you use `operator <<` instead of `printf` ? `std::cout << "I am a hex: " << std::hex << 5;` ? – Jarod42 Oct 17 '17 at 08:19
  • 1
    `printf` uses `va_list`/`va_start` – Barmak Shemirani Oct 17 '17 at 08:20
  • @Jarod42 In this specific instance I could indeed, but I have a code base at hand that uses this kind of print(const char* fmt, ...) a couple thousand times and am not too keen on rewriting every occurrence. – vonludi Oct 17 '17 at 08:23
  • 2
    In C++ you would use variadic templates [like this](https://stackoverflow.com/questions/17671772/c11-variadic-printf-performance). In C you use `...` and lose all type information and rely on the format specifier to get it back. – nwp Oct 17 '17 at 08:28

1 Answers1

2

fprintf is a function inherited from C. In particular, it takes a C variadic argument: int fprintf(FILE*, const char*, ...).

You can only pass built-in C types to such a function, not even user-defined C types. Attempting to do so is Undefined Behavior. Trying to pass a MyType is dangerous (e.g. the string you mention is not allowed). Passing a std::initializer_list<T> is always Undefined Behavior.

As Jarod states in the comments, use std::cout <<. If you need fancier formatting, use Boost.format.

MSalters
  • 173,980
  • 10
  • 155
  • 350