Deprecating functions and methods in C++

Refactoring a C++ code base for a binary API, I needed to deprecate some functions and communicate changes to API users. While noted in the API’s documentation, people don’t read documentation, so I wanted the compiler to warn users as well (not that people read compiler warnings either!).

C++14 has a [[deprecated]] attribute, but what if you want to use that if supported, and do something else for other compilers?

Macros to the rescue! Below will use the canonical C++14 way if supported, and an equivalent proprietary method for GCC and Microsoft Visual C++ respectively.

// Helper to deprecate functions and methods
// See https://blog.samat.io/2017/02/27/Deprecating-functions-and-methods-in-Cplusplus/
// For C++14
#if __cplusplus >= 201402L
    #if defined(__has_cpp_attribute)
        #if __has_cpp_attribute(deprecated)
            #define DEPRECATED(msg, func) [[deprecated(msg)]] func
        #endif
    #endif
// For everyone else
#else
    #ifdef __GNUC__
        #define DEPRECATED(msg, func) func __attribute__ ((deprecated(msg)))
    #elif defined(_MSC_VER)
        #define DEPRECATED(msg, func) __declspec(deprecated(msg)) func
    #endif
#endif

To use it: in your header file for you API, simply wrap a function or method declaration with the macro. From:

void go(size_t goRadius, float one, float two, float three);

wrap it like so:

DEPRECATED("Use goNew()", void go(size_t goRadius, float one, float two, float three));

And you’ll get a warning. Here’s what it looks like with GCC 6.2:

/api.cpp: In member function ‘void SomeClass::go()’:
/api.cpp:104:23: warning: ‘void SomeClass::go(size_t, float, float, float)’ is deprecated: Use goNew() [-Wdeprecated-declarations]
     go(10, 1, 1, 1);
                   ^
In file included from /api.cpp:17:0:
/api.h:135:37: note: declared here
     DEPRECATED("Use goNew()", void go(size_t goRadius, float one, float two, float three));
                                    ^
/api.h:41:63: note: in definition of macro ‘DEPRECATED’
             #define DEPRECATED(msg, func) [[deprecated(msg)]] func
                                                           ^~~~

The macro is not perfect, however (or rather, compilers are not).

The canonical way for checking if[[deprecated]]is supported is with the compiler definition__has_cpp_attribute(deprecated); unfortunately, GCC 6.2 defines this symbol regardless of whether you are in C++14 mode or not. And then it prints a warning when run in-pedanticmode, even though it’s supported!

In the above snippet, the C++14 method is only used if the compiler fully supports C++14 and is in C++14 mode. If that’s not important to you, consider removing that extra if statement, and use this instead:

// Helper to deprecate functions and methods
// See https://blog.samat.io/2017/02/27/Deprecating-functions-and-methods-in-Cplusplus/
// For C++14
#if defined(__has_cpp_attribute)
    #if __has_cpp_attribute(deprecated)
        #define DEPRECATED(msg, func) [[deprecated(msg)]] func
    #endif
// For everyone else
#else
    #ifdef __GNUC__
        #define DEPRECATED(msg, func) func __attribute__ ((deprecated(msg)))
    #elif defined(_MSC_VER)
        #define DEPRECATED(msg, func) __declspec(deprecated(msg)) func
    #endif
#endif

Comments

Comments powered by Disqus