6

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.

9
  • 1
    If you can use C++20, there is std::print and std::format which give you printf like syntax with C++ variadic templates and type safety for printing and string creation respectively. Commented Feb 25, 2023 at 14:29
  • godbolt.org/z/Th36nPnq9 Commented Feb 25, 2023 at 14:34
  • godbolt.org/z/aq5KndYd8 Commented Feb 25, 2023 at 14:43
  • 2
    Then use this library : github.com/fmtlib/fmt Commented Feb 25, 2023 at 15:29
  • 2
    Clang seems to work properly using [[gnu::format(printf, 1, 2)]]. Commented Feb 27, 2023 at 9:09

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.