Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

__has_cpp_attribute not a 'function-like' macro?

I am attempting to introduce the [[deprecated]] attribute into my codebase. However, not all the compilers I am required to support have support for this syntax yet (the various method used by different compilers before standardization are described in the attribute standardization proposal N2761). Thus, I am attempting to conditionally compile in this attribute, using the __has_cpp_attribute macro-like function first, if it is available, like so:

#if defined(__has_cpp_attribute) && __has_cpp_attribute(deprecated)
    #define DEPRECATED(msg) [[deprecated(msg)]]
#elif OTHER_COMPILER
    // ...
#endif

However, I'm getting errors when compiling this I am using gcc version 4.9.2 (GCC), command line gcc -std=c++14 cpp.cpp:

cpp.cpp:1:56: error: missing binary operator before token "("
#if defined(__has_cpp_attribute) && __has_cpp_attribute(deprecated)

This error seems to indicate that __has_cpp_attribute is defined, but that it is not a macro function. What is the proper way to conditionally compile the [[deprecated]] attribute in gcc?

like image 670
MuertoExcobito Avatar asked Dec 06 '22 21:12

MuertoExcobito


2 Answers

GCC 4.9 doesn't have __has_cpp_attribute, and the short-circuiting behavior of && does not extend to allowing invalid constructs to follow it.

That is to say, if foo isn't defined,

#if defined(foo) && foo(bar)

is not valid.

What you want is

#if defined(__has_cpp_attribute) 
    #if __has_cpp_attribute(deprecated)
        #define DEPRECATED(msg) [[deprecated(msg)]]
    #endif
#elif OTHER_COMPILER
    // ...
#endif

so that the condition using __has_cpp_attribute is in a group that is skipped if __has_cpp_attribute is not defined. (When in a group that is skipped, preprocessing directives are only processed through the directive's name; the remaining tokens are ignored.)

like image 142
T.C. Avatar answered Dec 31 '22 00:12

T.C.


T.C.'s answer is right, I wanted to add that you can also define a wrapper for __has_cpp_attribute, which tends to make multiple tests easier to write

#if defined(__has_cpp_attribute)
#  define MY_HAS_CPP_ATTRIBUTE(attr) __has_cpp_attribute(attr)
#else
#  define MY_HAS_CPP_ATTRIBUTE(attr) (0)
#endif

#if MY_HAS_CPP_ATTRIBUTE(attr)
#  define MY_DEPRECATED [[deprecated(msg)]]
#else
#  define MY_DEPRECATED
#endif

If you go this route, please use a namespace instead of defining __has_cpp_attribute. Other code will check to see if defined(__has_cpp_attribute) and use it if available or fall back on checking compiler versions if it isn't. Defining __has_cpp_attribute will break that.

Of course, a lot of other compilers support some kind of syntax for marking symbols as deprecated even if they don't support __has_cpp_attribute, so you'll probably end up with a lot more cases; see the HEDLEY_DEPRECATED macro in Hedley. It currently supports GCC 4.5+, ICC 13+, armcc 4.1+, and TI 7.3+ using the deprecated attribute, MSVC 13.10+ and Pelles 6.50+ using the deprecated declspec, and IAR using a pragma, but it's likely that support will expand over time and I probably won't update this answer.

like image 34
nemequ Avatar answered Dec 31 '22 02:12

nemequ