If I compile g++ with -Wformat on the following code, I get a warning, as expected.
int main(int argc, char * argv[])
{
printf("argc %lld\n", argc);
}
However, if compile the following code, I get no warning.
template <typename... Ts>
void foo(char const * s, Ts && ... ts)
{
printf(s, std::forward<Ts>(ts)...);
}
int main(int argc, char * argv[])
{
foo("argc %lld\n", argc);
}
I was a bit surprised, but I thought maybe the references were throwing off the detection, so I tried this.
template <typename... Ts>
void foo(char const * s, Ts ... ts)
{
printf(s, ts...);
}
int main(int argc, char * argv[])
{
foo("argc %lld\n", argc);
}
However, even with that, I get no warning. Hmmm.
Since I'm compiling with C++17, I tried this.
template <typename... Ts>
[[gnu::format(printf, 1, 2)]]
void foo(char const * s, Ts ... ts)
{
printf(s, ts...);
}
int main(int argc, char * argv[])
{
foo("bar %lld\n", argc);
printf("bar %lld\n", argc);
}
I still get no warning, but I do get a nice error...
error: 'format' attribute argument 3 value '2' does not refer to a variable argument list
OK, I get it, but if I have to change to a variable argument list, then I lose the type information.
The real issue is that I have a function kinda like this...
template <typename T, typename... ArgTs>
void format(T t, char const * format_str, ArgTs && ... args);
In that function, I do some stuff with T and ArgTs, but I want the same printf style warnings I would get if I were to call printf with the format_str and args. I need the type information provided by the arguments, so I can't just use a variable argument list.
Of course, I can write an empty function that takes a variable argument list, and annotate it with [[gnu::format]], but that behaves the exact same way as calling printf() above, so it only works if called directly with a variable argument list, and does not work if called with a variadic argument pack expansion.
So, how do I get the benefit of [[gnu::format]]
with a function that does not take its arguments by variable argument list?
I could use a macro, of course, and have it call a do-nothing function with the [[gnu::format]]
attribute, then call the real one, but I don't want to use a macro for this.
std::print
andstd::format
which give you printf like syntax with C++ variadic templates and type safety for printing and string creation respectively. – Mourning[[gnu::format(printf, 1, 2)]]
. – Metameric