42

I'm trying to define a class method for debug prints that will behave like printf:

inline void debug(const char* fmt, ...) __attribute__ ((format (printf, 1, 2)))

When I compile with -Wformat or -Wall, This complains about:

error: format string argument not a string type

I recalled that a class method declaration has an implicit this parameter, so I changed the locations of the parameters to 2, 3:

inline void debug(const char* fmt, ...) __attribute__ ((format (printf, 2, 3)))

and now it compiles, but it looks like the parameters are shifted, as if the this parameter were being treated as part of the argument list.

How can I tell the function that this isn't part of the string that I want to print?

Swiss Frank
  • 1,985
  • 15
  • 33
Nathan Fellman
  • 122,701
  • 101
  • 260
  • 319
  • Can you use variadic templates? If so, you can make a [type safe printf](http://www.generic-programming.org/~dgregor/cpp/variadic-templates.html) – chris Jul 23 '12 at 21:48
  • 2
    Don't think too much about `this`. It's not an explicit argument, period. Just follow the GCC manual, which says that for member functions you have to add 1 to the `format`-attribute arguments. It's just an opaque rule, given to you by the vendor of a compiler extension. – Kerrek SB Jul 23 '12 at 21:50
  • 3
    printf (2, 3) is right. Define "seems like shifted"...? –  Jul 23 '12 at 21:51
  • The `format` attribute specifies style of the format string, the argument location of the format string, and the argument location of the `...`. The compiler then uses that information to do type checking of the `...` arguments with the format string. – jxh Jul 23 '12 at 22:09
  • Uh! And even with gcc 6.2 the error is still as cryptic... – Matthieu M. Oct 03 '16 at 15:59
  • I got the same error when i added __ attribute __ after the semicolon. Later i realized this error and added semicolon after attribute declaration. Strange! – Nasir Mar 16 '17 at 13:24

4 Answers4

40

You've done it. this is argument 1, so by saying format(printf, 2, 3) you're telling the compiler that you're NOT printing this, you're printing argument 2 (fmt) with additional arguments past that.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
  • It makes sense but I wonder why this is not documented anywhere – dashesy Jun 15 '17 at 17:14
  • 6
    you are right [here](https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes): **Since non-static C++ methods have an implicit this argument, the arguments of such methods should be counted from two, not one, when giving values for string-index and first-to-check.** – dashesy Jun 16 '17 at 18:14
5

Treat static members the same as non-members. The discussion gave me the answer, but it's worth noting for others:

  • non-member functions work with 1,2
  • static member functions work with 1,2
  • non-static member functions treat 'this' as #1, so need 2,3

I found this because we have some processes that use log helpers like this and 1 out of 4 was requiring __attribute__ (( format( printf, 2, 3 ) )) with the other three working well with __attribute__ (( format(printf, 1, 2) )) - turned out it was non-static...

sage
  • 4,863
  • 2
  • 44
  • 47
4

@Chris Dodd is correct. Here's the latest gcc documentation to back it up (thanks Foxit reader for letting me mark up PDFs on Linux). Pay special attention to the part marked in green in the image below.

Since non-static C++ methods have an implicit this argument, the arguments of such methods should be counted from two, not one, when giving values for string-index and first-to-check.

Source: https://gcc.gnu.org/onlinedocs/gcc-8.2.0/gcc/Common-Function-Attributes.html#Common-Function-Attributes (see the section titled "format (archetype, string-index, first-to-check)").

Image (esp. see highlighting in green):

enter image description here

Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
2

Since it only works for gcc, it would be good to define it this way to avoid errors on other compilers.

#ifdef __GNUC__
          __attribute__ (( format( printf, 2, 3 ) ))
#endif
Don Carr
  • 321
  • 2
  • 2
  • 2
    That's a good point in general, but it's not an issue for me, as this is an internal code base built using a strict flow. – Nathan Fellman Sep 09 '13 at 06:55
  • 6
    You could also use `#ifndef __GNUC__ #define __attribute__(a)`. Then you can use any attribute. – cubuspl42 Apr 17 '14 at 18:03
  • 1
    It works for clang too, actually, which is, by design, gcc compatible. "It is designed to act as a drop-in replacement for the GNU Compiler Collection (GCC), supporting most of its compilation flags and unofficial language extensions." ([Wikipedia](https://en.wikipedia.org/wiki/Clang)). – Gabriel Staples Apr 28 '20 at 21:57