Samat Says (Posts about C++)https://blog.samat.org/tag/c%2B%2B.atom2018-06-29T09:25:27ZSamat K JainNikolaDeprecating functions and methods in C++https://blog.samat.org/2017/02/27/Deprecating-functions-and-methods-in-Cplusplus/2017-02-27T00:00:00Z2017-02-27T00:00:00ZSamat K Jain<div><p>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!).</p>
<p><a class="reference external" href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3760.html">C++14 has a [[deprecated]] attribute</a>, but what if you want to use that if supported, and do something else for other compilers?</p>
<p>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.</p>
<pre class="code text"><a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-1"></a>// Helper to deprecate functions and methods
<a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-2"></a>// See https://blog.samat.io/2017/02/27/Deprecating-functions-and-methods-in-Cplusplus/
<a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-3"></a>// For C++14
<a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-4"></a>#if __cplusplus >= 201402L
<a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-5"></a> #if defined(__has_cpp_attribute)
<a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-6"></a> #if __has_cpp_attribute(deprecated)
<a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-7"></a> #define DEPRECATED(msg, func) [[deprecated(msg)]] func
<a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-8"></a> #endif
<a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-9"></a> #endif
<a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-10"></a>// For everyone else
<a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-11"></a>#else
<a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-12"></a> #ifdef __GNUC__
<a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-13"></a> #define DEPRECATED(msg, func) func __attribute__ ((deprecated(msg)))
<a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-14"></a> #elif defined(_MSC_VER)
<a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-15"></a> #define DEPRECATED(msg, func) __declspec(deprecated(msg)) func
<a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-16"></a> #endif
<a name="rest_code_c351d23e9ad04dfea0e052a0b7c738a3-17"></a>#endif
</pre><p>To use it: in your header file for you API, simply wrap a function or method declaration with the macro. From:</p>
<pre class="code c++"><a name="rest_code_30c4e1761b2e4bf6a545dcf719082d8e-1"></a><span class="kt">void</span> <span class="nf">go</span><span class="p">(</span><span class="kt">size_t</span> <span class="n">goRadius</span><span class="p">,</span> <span class="kt">float</span> <span class="n">one</span><span class="p">,</span> <span class="kt">float</span> <span class="n">two</span><span class="p">,</span> <span class="kt">float</span> <span class="n">three</span><span class="p">);</span>
</pre><p>wrap it like so:</p>
<pre class="code c++"><a name="rest_code_0cf2409ced7a473aa34c78fc5a2cc65e-1"></a><span class="n">DEPRECATED</span><span class="p">(</span><span class="s">"Use goNew()"</span><span class="p">,</span> <span class="kt">void</span> <span class="n">go</span><span class="p">(</span><span class="kt">size_t</span> <span class="n">goRadius</span><span class="p">,</span> <span class="kt">float</span> <span class="n">one</span><span class="p">,</span> <span class="kt">float</span> <span class="n">two</span><span class="p">,</span> <span class="kt">float</span> <span class="n">three</span><span class="p">));</span>
</pre><p>And you'll get a warning. Here's what it looks like with GCC 6.2:</p>
<pre class="code text"><a name="rest_code_723ede9bf13843dfadffe6b2a928284c-1"></a>/api.cpp: In member function ‘void SomeClass::go()’:
<a name="rest_code_723ede9bf13843dfadffe6b2a928284c-2"></a>/api.cpp:104:23: warning: ‘void SomeClass::go(size_t, float, float, float)’ is deprecated: Use goNew() [-Wdeprecated-declarations]
<a name="rest_code_723ede9bf13843dfadffe6b2a928284c-3"></a> go(10, 1, 1, 1);
<a name="rest_code_723ede9bf13843dfadffe6b2a928284c-4"></a> ^
<a name="rest_code_723ede9bf13843dfadffe6b2a928284c-5"></a>In file included from /api.cpp:17:0:
<a name="rest_code_723ede9bf13843dfadffe6b2a928284c-6"></a>/api.h:135:37: note: declared here
<a name="rest_code_723ede9bf13843dfadffe6b2a928284c-7"></a> DEPRECATED("Use goNew()", void go(size_t goRadius, float one, float two, float three));
<a name="rest_code_723ede9bf13843dfadffe6b2a928284c-8"></a> ^
<a name="rest_code_723ede9bf13843dfadffe6b2a928284c-9"></a>/api.h:41:63: note: in definition of macro ‘DEPRECATED’
<a name="rest_code_723ede9bf13843dfadffe6b2a928284c-10"></a> #define DEPRECATED(msg, func) [[deprecated(msg)]] func
<a name="rest_code_723ede9bf13843dfadffe6b2a928284c-11"></a> ^~~~
</pre><p>The macro is not perfect, however (or rather, compilers are not).</p>
<p>The canonical way for checking if <tt class="docutils literal">[[deprecated]]</tt> is supported is with the compiler definition <tt class="docutils literal">__has_cpp_attribute(deprecated)</tt>;
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 <tt class="docutils literal"><span class="pre">-pedantic</span></tt> mode, even though it's supported!</p>
<p>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:</p>
<pre class="code text"><a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-1"></a>// Helper to deprecate functions and methods
<a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-2"></a>// See https://blog.samat.io/2017/02/27/Deprecating-functions-and-methods-in-Cplusplus/
<a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-3"></a>// For C++14
<a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-4"></a>#if defined(__has_cpp_attribute)
<a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-5"></a> #if __has_cpp_attribute(deprecated)
<a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-6"></a> #define DEPRECATED(msg, func) [[deprecated(msg)]] func
<a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-7"></a> #endif
<a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-8"></a>// For everyone else
<a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-9"></a>#else
<a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-10"></a> #ifdef __GNUC__
<a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-11"></a> #define DEPRECATED(msg, func) func __attribute__ ((deprecated(msg)))
<a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-12"></a> #elif defined(_MSC_VER)
<a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-13"></a> #define DEPRECATED(msg, func) __declspec(deprecated(msg)) func
<a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-14"></a> #endif
<a name="rest_code_cee5cb2aafe44483a53c7aaab9fc237c-15"></a>#endif
</pre></div>