c++macrosc++20clang++openvdb

How to detect C++20 mode of compilation?


When compiling my program using a recent version of OpenVDB library by Clang in C++20 mode, I get the error:

error: use of the 'assume' attribute is a C++23 extension

I see that it is due to the macro OPENVDB_ASSUME conditionally defined as (simplified):

#ifdef __has_cpp_attribute
  #if __has_cpp_attribute(assume) >= 202207L
    #define OPENVDB_ASSUME(...) [[assume(__VA_ARGS__)]]
  #endif
#endif
#ifndef OPENVDB_ASSUME
  #define OPENVDB_ASSUME(...) __builtin_assume(__VA_ARGS__)
#endif

Despite the condition __has_cpp_attribute(assume) >= 202207L, the macro still expands in [[assume]] in C++20 mode. Online demo: https://gcc.godbolt.org/z/fEY5GWc9P

How one has to modify the condition to prevent it, and get C++20 compatible __builtin_assume(__VA_ARGS__) expansion instead?


Solution

  • It is really unfortunate that Clang decided to put users in this position. Since the intent of

    #if __has_cpp_attribute(attr)
    

    Was that if that returns non-zero, that you can... actually use the attribute. Warning after a check means that you suddenly need far more information to do a proper check.

    Now, this answer suggests writing:

    #if __has_cpp_attribute(assume) && __cplusplus >= __has_cpp_attribute(assume)
    

    This is actually incorrect. The problem here is that the value that __has_cpp_attribute(attr) returns isn't fixed. It might change. For instance, [[nodiscard]] was an attribute that was added in C++17, initially with the value 201603. Then, during C++20, you were allowed to add a reason to it, so it now uses the value 201907.

    Imagine I applied the above check to [[nodiscard]]:

    #if __has_cpp_attribute(nodiscard) && __cplusplus >= __has_cpp_attribute(nodiscard)
    #  define NODISCARD [[nodiscard]]
    #else
    #  define NODISCARD
    #endif
    

    If I'm compiling with clang 9 in c++17 mode, I would get [[nodiscard]]. But with clang 10 in c++17 mode, I would suddenly get nothing — because clang 10 implemented the added [[nodiscard]] functionality, so now our check fails. And we don't get any [[nodiscard]] at all! For example:

    #if __has_cpp_attribute(nodiscard) && __cplusplus >= __has_cpp_attribute(nodiscard)
    #  define NODISCARD [[nodiscard]]
    #else
    #  define NODISCARD
    #endif
    
    NODISCARD int f() { return 0; }
    
    int main() {
        f(); // warns with clang 9, not with clang 10+
    }
    

    Instead, you just have to know.

    That is, the specific check you need to write for [[nodiscard]] is this:

    #if __has_cpp_attribute(nodiscard) && __cplusplus >= 201603
    

    And likewise the specific check you have to write for assume is

    #if __has_cpp_attribute(assume) && __cplusplus >= 202207
    

    Thankfully, you can at least easily find these values in the feature-test document (also via the link wg21.link/sd6). But it would have been nice to not have to do this.