C++11 brought support for so-called "generalized attributes", along with two standard ones, noreturn
and carries_dependency
. C++14 added deprecated
to the table. However, I cannot find anything clear on what are implementations mandated to do by the standard upon finding an attribute that's unknown to them. Perhaps it's just that the standard doesn't say anything on the matter. Nevertheless, I would like to know.
In practice, both gcc and clang have warnings regarding this situation that may be disabled. In particular, gcc gives the warning 'some_attribute' attribute directive ignored
(-Wattributes
), while clang complains with unknown attribute 'some_attribute' ignored
(-Wunknown-attributes
). I haven't been able to find documentation for those options, though; the Warning Options page for GCC merely mentions -Wattributes
, while there doesn't even seem to be a similar list for Clang.
Why do I ask this? Well, some implementation-specific attributes are interesting. Take, for example, GCC's nonnull
attribute. We could happily annotate some functions with it:
[[gnu::nonnull]] void my_function ( char * a , int * b );
If the implementation knows about the attribute, everything's fine. But what can we expect if it doesn't?
It ignores it and this behavior is backed by the standard. We can go on and add every interesting attribute we find in online docs. In particular, this would allow mixing
visibility
anddllexport
without as many#if
s ~yay!#if BUILDING_DLL #define DLLEXIMPORT dllexport #else #define DLLEXIMPORT dllimport #endif // even if MSVC doesn't support specifying dllexport this way, Clang does, and it can // compile MSVC-compatible objects [[gnu::visibility("default"),DLLEXIMPORT]] void api_function ();
It complains about it and this behavior is backed by the standard. D'oh! We'll have to roll some ugly machinery to keep unknown attributes from the hands of those pesky implementations.
With just one attribute, it's easy, as empty attribute lists are allowed by the standard:
#if SOMETHING #define ATTRIBUTE_GNU_NONNULL gnu::nonnull #else #define ATTRIBUTE_GNU_NONNULL #endif // if ATTRIBUTE_GNU_NONNULL is defined to nothing, all that happens is that we get // an empty attribute list, which is 'standardly valid' [[ATTRIBUTE_GNU_NONNULL]] void my_function ( char * a , int * b );
However, with more than one attribute, things become more tricky:
#if SOMETHING #define ATTRIBUTE_GNU_NONNULL gnu::nonnull #else #define ATTRIBUTE_GNU_NONNULL #endif #if SOMETHING #define ATTRIBUTE_GNU_VISIBILITY( visibility_type ) gnu::visibility(#visibility_type) #else #define ATTRIBUTE_GNU_VISIBILITY( visibility_type ) #endif // if any of the two macros is defined to nothing, it expands to [[,blah_blah]] // or [[blah_blah,]] - both of which are invalid [[ATTRIBUTE_GNU_NONNULL,ATTRIBUTE_GNU_VISIBILITY(default)]] void my_function ( char * a , int * b );
The even-uglier solution that comes to mind is to have macros that combine attributes, like this:
#if SOMETHING #define ATTRIBUTES_GNU_NONNULL_AND_GNU_VISIBILITY( visibility_type ) \ gnu::nonnull,gnu::visibility(#visibility_type) #else #define ATTRIBUTES_GNU_NONNULL_AND_GNU_VISIBILITY( visibility_type ) #endif [[ATTRIBUTES_GNU_NONNULL_AND_GNU_VISIBILITY(default)]] void my_function ( char * a , int * b );
But I would be shot in the head for this
:)
.
Thus, if the standard mandated that implementations ignore unknown attributes, I could pick every relevant attribute I found in online docs without fear and without ugly solutions — hence my interest.