GCC Format attribute not working on function pointer with "using" alias

100 views Asked by At

As the ISO C++ Guidelines recommends, we should use using instead of typedef. However, recently I had to code some debug logging where the attribute would be helpful for compile-time diagnostics.

When I tried applying the using keyword, this did not seem to work:

using logger_cb = void(*)(const char*, va_list) 
__attribute__ ((__format__ (__printf__, 1, 0)));

The error is:

<source>:4:48: error: expected ';' before '__attribute__'
    4 | using logger_cb = void(*)(const char*, va_list) __attribute__ ((__format__ (__printf__, 1, 0))); // <-- uncommenting this won't work
      |                                                ^~~~~~~~~~~~~~
      |                                                ;

However, using typedef it works:

typedef void (*logger_cb)(const char*, va_list) 
__attribute__ ((__format__ (__printf__, 1, 0)));

I cannot figure out if my syntax is wrong or this simply is not supported with the using keyword. Does some guru know about this?

Link to example: https://godbolt.org/z/qWdxWq6Gx

2

There are 2 answers

2
user12002570 On BEST ANSWER

When I tried applying the using keyword, this did not seem to work:

The placement of the optional attribute-specifier-seq must be after the identifier in an alias-declaration. This can be seen from alias declaration:

Alias declarations are declarations with the following syntax:

using identifier attr (optional) = type-id ;  (1)     
template < template-parameter-list >
using identifier attr (optional) = type-id ;  (2)      

attr - optional sequence of any number of attributes

The same can be seen from gram.dcl:

alias-declaration:
using identifier attribute-specifier-seqopt = defining-type-id ; 

This means that the standard way to use using here is as shown below:

using logger_cb [[gnu::format (__printf__, 1, 0) ]] = void(*)(const char*, va_list);

Working demo

2
Artyer On

If you switch to C++11 attribute syntax, it goes in that position to apply to the function type void(const char*, va_list):

using logger_cb = void (*)(const char*, va_list)
[[__gnu__::__format__(__printf__, 1, 0)]];

This is where standard attributes that apply to the function types are "supposed" to go, but this alternative position works for the __attribute__ syntax too:

using logger_cb = void ([[__gnu__::__format__(__printf__, 1, 0)]] *)(const char*, va_list);
using logger_cb = void (__attribute__ ((__format__ (__printf__, 1, 0))) *)(const char*, va_list);

(with the caveat that this only works because its before a *, and wouldn't work if logger_cb was an alias for a function type)


You can also apply it to the declaration (logger_cb):

using logger_cb [[__gnu__::__format__(__printf__, 1, 0)]]
    = void (*)(const char*, va_list);
using logger_cb __attribute__ ((__format__ (__printf__, 1, 0)))
    = void (*)(const char*, va_list);

Or to the pointer type void(*)(const char*, va_list):

using logger_cb = void (* [[__gnu__::__format__(__printf__, 1, 0)]])(const char*, va_list);
using logger_cb = void (* __attribute__ ((__format__ (__printf__, 1, 0))))(const char*, va_list);

Since they all end up doing the same thing for the __format__ attribute, but note that this isn't the case for other attributes.