I have a method in a C++ interface that I want to deprecate, with portable code.
When I Googled for this all I got was a Microsoft specific solution; #pragma deprecated and __declspec(deprecated).
If a general or fully-portable deprecation solution is not available, I will accept as a "second prize solution" one that can be used multiple specific compilers, like MSVC and a GCC.
In C++14, you can mark a function as deprecated using the [[deprecated]] attribute (see section 7.6.5 [dcl.attr.deprecated]).
The attribute-token deprecated can be used to mark names and entities whose use is still allowed, but is discouraged for some reason.
For example, the following function foo is deprecated:
[[deprecated]]
void foo(int);
It is possible to provide a message that describes why the name or entity was deprecated:
[[deprecated("Replaced by bar, which has an improved interface")]]
void foo(int);
The message must be a string literal.
For further details, see “Marking as deprecated in C++14”.
This should do the trick:
#ifdef __GNUC__
#define DEPRECATED(func) func __attribute__ ((deprecated))
#elif defined(_MSC_VER)
#define DEPRECATED(func) __declspec(deprecated) func
#else
#pragma message("WARNING: You need to implement DEPRECATED for this compiler")
#define DEPRECATED(func) func
#endif
...
//don't use me any more
DEPRECATED(void OldFunc(int a, float b));
//use me instead
void NewFunc(int a, double b);
However, you will encounter problems if a function return type has a commas in its name e.g. std::pair<int, int> as this will be interpreted by the preprocesor as passing 2 arguments to the DEPRECATED macro. In that case you would have to typedef the return type.
Edit: simpler (but possibly less widely compatible) version here.
Here's a simplified version of my 2008 answer:
#if defined(__GNUC__) || defined(__clang__)
#define DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER)
#define DEPRECATED __declspec(deprecated)
#else
#pragma message("WARNING: You need to implement DEPRECATED for this compiler")
#define DEPRECATED
#endif
//...
//don't use me any more
DEPRECATED void OldFunc(int a, float b);
//use me instead
void NewFunc(int a, double b);
See also:
MSVC documentation for __declspec(deprecated)
GCC documentation for __attribute__((deprecated))
Clang documentation for __attribute__((deprecated))
In GCC you can declare your function with the attribute deprecated like this:
void myfunc() __attribute__ ((deprecated));
This will trigger a compile-time warning when that function is used in a .c file.
You can find more info under "Diagnostic pragmas" at
http://gcc.gnu.org/onlinedocs/gcc/Pragmas.html
Here is a more complete answer for 2018.
These days, a lot of tools allow you to not just mark something as deprecated, but also provide a message. This allows you to tell people when something was deprecated, and maybe point them toward a replacement.
There is still a lot of variety in compiler support:
C++14 supports [[deprecated]]/[[deprecated(message)]].
__attribute__((deprecated)) is supported by GCC 4.0+ and ARM 4.1+
__attribute__((deprecated)) and __attribute__((deprecated(message))) is supported for:
GCC 4.5+
Several compilers which masquerade as GCC 4.5+ (by setting __GNUC__/__GNUC_MINOR__/__GNUC_PATCHLEVEL__)
Intel C/C++ Compiler going back to at least 16 (you can't trust __GNUC__/__GNUC_MINOR__, they just set it to whatever version of GCC is installed)
ARM 5.6+
MSVC supports __declspec(deprecated) since 13.10 (Visual Studio 2003)
MSVC supports __declspec(deprecated(message)) since 14.0 (Visual Studio 2005)
You can also use [[gnu::deprecated]] in recent versions of clang in C++11, based on __has_cpp_attribute(gnu::deprecated).
I have some macros in Hedley to handle all of this automatically which I keep up to date, but the current version (v2) looks like this:
#if defined(__cplusplus) && (__cplusplus >= 201402L)
# define HEDLEY_DEPRECATED(since) [[deprecated("Since " #since)]]
# define HEDLEY_DEPRECATED_FOR(since, replacement) [[deprecated("Since " #since "; use " #replacement)]]
#elif \
HEDLEY_GCC_HAS_EXTENSION(attribute_deprecated_with_message,4,5,0) || \
HEDLEY_INTEL_VERSION_CHECK(16,0,0) || \
HEDLEY_ARM_VERSION_CHECK(5,6,0)
# define HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since)))
# define HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement)))
#elif \
HEDLEY_GCC_HAS_ATTRIBUTE(deprcated,4,0,0) || \
HEDLEY_ARM_VERSION_CHECK(4,1,0)
# define HEDLEY_DEPRECATED(since) __attribute__((__deprecated__))
# define HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__))
#elif HEDLEY_MSVC_VERSION_CHECK(14,0,0)
# define HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since))
# define HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement))
#elif HEDLEY_MSVC_VERSION_CHECK(13,10,0)
# define HEDLEY_DEPRECATED(since) _declspec(deprecated)
# define HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated)
#else
# define HEDLEY_DEPRECATED(since)
# define HEDLEY_DEPRECATED_FOR(since, replacement)
#endif
I'll leave it as an exercise to figure out how to get rid of the *_VERSION_CHECK and *_HAS_ATTRIBUTE macros if you don't want to use Hedley (I wrote Hedley largely so I wouldn't have to think about that on a regular basis).
If you use GLib, you can use the G_DEPRECATED and G_DEPRECATED_FOR macros. They're not as robust as the ones from Hedley, but if you already use GLib there is nothing to add.
Dealing with portable projects it's almost inevitable that you at some point need a section of preprocessed alternatives for a range of platforms. #ifdef this #ifdef that and so on.
In such a section you could very well conditionally define a way to deprecate symbols. My preference is usually to define a "warning" macro since most toolchains support custom compiler warnings. Then you can go on with a specific warning macro for deprecation etc.
For the platforms supporting dedicated deprecation methods you can use that instead of warnings.
For Intel Compiler v19.0, use this as __INTEL_COMPILER evaluates to 1900:
# if defined(__INTEL_COMPILER)
# define DEPRECATED [[deprecated]]
# endif
Works for the following language levels:
C++17 Support (/Qstd=c++17)
C++14 Support (/Qstd=c++14)
C++11 Support (/Qstd=c++11)
C11 Support (/Qstd=c11)
C99 Support (/Qstd=c99)
The Intel Compiler has what appears a bug in that it does not support the [[deprecated]] attribute on certain language elements that all other compilers do. For an example, compile v6.0.0 of the (remarkly superb) {fmtlib/fmt} library on GitHub with Intel Compiler v19.0. It will break. Then see the fix in the GitHub commit.
Related
Various modern C/C++ compilers include one or both of __func__ / __FUNCTION__ for purposes of logging the currently executing function. MSVC++ also includes __FUNCSIG__ and GCC __PRETTY_FUNCTION__ as compiler-specific enhanced flavors of this functionality.
GCC defines these as variables rather than macros, however, so it isn't possible to test for their presence via a #ifdef preprocessor directive.
I'm working with a codebase that must work with C++98 and C++11 flavors of MSVC++ and GCC, and the logging facility that someone wrote erroneously tries to test for __FUNCTION__ if __FUNCSIG__ is not available. This check always returns false, rendering function logging support non-operational.
Question: Is there a good macro out there that makes a sufficient-for-my-use-cases guess at which (if any) of these features should be present, possibly by sniffing out compiler versions?
T.C. supplied this answer first, as a comment under my question, and I ended up basing my solution on it:
The header <boost/current_function.hpp> in Boost.Assert implements a BOOST_CURRENT_FUNCTION macro that attempts to map to a suitable "current function" facility provided by the compiler.
Documentation is here:
http://www.boost.org/doc/libs/1_66_0/libs/assert/doc/html/assert.html#current_function_macro_boost_current_function_hpp
And here is a concise reproduction of the macro for reference:
#if defined( BOOST_DISABLE_CURRENT_FUNCTION )
# define BOOST_CURRENT_FUNCTION "(unknown)"
#elif defined(__GNUC__) || (defined(__MWERKS__) && (__MWERKS__ >= 0x3000)) || (defined(__ICC) && (__ICC >= 600)) || defined(__ghs__)
# define BOOST_CURRENT_FUNCTION __PRETTY_FUNCTION__
#elif defined(__DMC__) && (__DMC__ >= 0x810)
# define BOOST_CURRENT_FUNCTION __PRETTY_FUNCTION__
#elif defined(__FUNCSIG__)
# define BOOST_CURRENT_FUNCTION __FUNCSIG__
#elif (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 600)) || (defined(__IBMCPP__) && (__IBMCPP__ >= 500))
# define BOOST_CURRENT_FUNCTION __FUNCTION__
#elif defined(__BORLANDC__) && (__BORLANDC__ >= 0x550)
# define BOOST_CURRENT_FUNCTION __FUNC__
#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901)
# define BOOST_CURRENT_FUNCTION __func__
#elif defined(__cplusplus) && (__cplusplus >= 201103)
# define BOOST_CURRENT_FUNCTION __func__
#else
# define BOOST_CURRENT_FUNCTION "(unknown)"
#endif
I ended up modifying the codebase I was working with to use the Boost macro when available, and to fall back to a reasonable subset of the Boost checks otherwise (GCC > MSVC++ > C++11).
__func__ was specified in C99 (not C++) and gcc has provided __FUNCTION__ and __PRETTY_FUNCTION__ for quite a while. However, none are actually macros, so can't be used as such. Among other things, that means you can't test for them using #ifdef.
To test if you are compiling as C++, and to what standard, use __cplusplus which is specified in the C++ standard, and expands to values 199711L (before C++11), 201103L (C++11), 201402L (C++14), and 201703L (C++17). The values correspond to the dates (year and month) when the standardisation committee approved the standard (e.g. 201703L means that C++17 was approved in March 2017). Note this is macro is only defined when compiling C++, not C.
Beyond that, to use features specific to a compiler, you need to test for the compiler that supports the features you want, not the feature itself (although some compilers/preprocessors do allow testing for particular language features, that's patchy in practice).
gcc and g++ (and other compilers that claim to support gnu C dialects) provide a number of specific macros, so you can check for them including
__GNUC__, defined for the GNU C compiler (can be tested using #ifdef). For gcc version x.y.z, __GNUC__ expands to the value x;
__GNUC_MINOR__, defined for the GNU C compiler (can be tested using #ifdef). For gcc version x.y.z, __GNUC_MINOR__ expands to the value y;
__GNUC_PATCH_LEVEL__, defined for the GNU C compiler (can be tested using #ifdef). For gcc version x.y.z, __GNUC_PATCHLEVEL__ expands to the value z;
__GNUG__ - defined for the g++ compiler.
An example of how you might use the gnu-specific macros is
#ifdef __GNUG__
/* Assume all versions of gcc that you use support __FUNCTION__ */
/* can use gcc's __FUNCTION__ here */
#endif
I don't know offhand which g++ versions started supporting __FUNCTION__ and __PRETTY_FUNCTION__, but if you want to test for particular version of the compiler, it's easy. For example, to test for g++ 3.2.0 or better
#ifdef __GNUG__
// test for g++ 3.2.0 or better
#if __GNUC__ > 3 || \
(__GNUC__ == 3 && (__GNUC_MINOR__ > 2 || \
(__GNUC_MINOR__ == 2 && \
__GNUC_PATCHLEVEL__ > 0))
// use features not introduced before g++ 3.2.0
#endif
#endif
For MSVC++, compiler-specific macros for a similar purpose include _MSC_VER which expands to an encoding of the major and minor versions. For example, under MSVC++ version 17.00.51106.1, _MSC_VER expands to 1700. You can find others on MSDN.
I am working on a project that has been built with both gcc and msvc so far. We recently started building with clang as well.
There are some parts in the code, where platform-specific things are done:
#ifndef _WIN32
// ignore this in msvc
#endif
Since gcc has previously been the only non-windows build, this was equivalent to saying "do this only for gcc". But now it means "do this only for gcc and clang".
However there are still situations, where I would like to handle something specifically for gcc, and not for clang. Is there a simple and robust way to detect gcc, i.e.
#ifdef ???
// do this *only* for gcc
#endif
__GNUC__
__GNUC_MINOR__
__GNUC_PATCHLEVEL__
These macros are defined by all GNU compilers that use the C preprocessor: C, C++, Objective-C and Fortran. Their values are the major version, minor version, and patch level of the compiler, as integer constants. For example, GCC 3.2.1 will define __GNUC__ to 3, __GNUC_MINOR__ to 2, and __GNUC_PATCHLEVEL__ to 1. These macros are also defined if you invoke the preprocessor directly.
Also:
__GNUG__
The GNU C++ compiler defines this. Testing it is equivalent to testing (__GNUC__ && __cplusplus).
Source
Apparently, clang uses them too. However it also defines:
__clang__
__clang_major__
__clang_minor__
__clang_patchlevel__
So you can do:
#ifdef __GNUC__
#ifndef __clang__
...
Or even better (note the order):
#if defined(__clang__)
....
#elif defined(__GNUC__) || defined(__GNUG__)
....
#elif defined(_MSC_VER)
....
I use this define:
#define GCC_COMPILER (defined(__GNUC__) && !defined(__clang__))
And test with it:
#if GCC_COMPILER
...
#endif
With Boost, this becomes very simple:
#include <boost/predef.h>
#if BOOST_COMP_GNUC
// do this *only* for gcc
#endif
See also the Using the predefs section of the boost documentation.
(credit to rubenvb who mentioned this in a comment, to Alberto M for adding the include, and to Frederik Aalund for correcting #ifdef to #if)
I have some code that requires a certain gcc compiler option (otherwise it won't compile). Of course, I can make sure in the makefile that for this particular source file the required option is set. However, it would much more helpful, if this option could be set for the respective compilation unit (or part of it) from within the source_file.cpp.
I know that warning messages can be switched on or off using #pragma GCC diagnostic, but what about the -fsomething type of options? I take it from this question that this is impossible.
But perhaps there is at least a way to check from within the code whether a certain -f option is on or not?
Note I'm not interested in finding the compiler flags from the binary, as was asked previously, nor from the command line.
In my experience, no. This is not the way you go about this. Instead, you put compiler/platform/OS specific code in your source, and wrap it with the appropriate ifdef statements. These include:
#ifdef __GNUC__
/*code for GNU C compiler */
#elif _MSC_VER
/*usually has the version number in _MSC_VER*/
/*code specific to MSVC compiler*/
#elif __BORLANDC__
/*code specific to borland compilers*/
#elif __MINGW32__
/*code specific to mingw compilers*/
#endif
Within this, you can have version-specific requirements and code:
#ifdef __GNUC__
# include <features.h>
# if __GNUC_PREREQ(4,0)
// If gcc_version >= 4.0
# elif __GNUC_PREREQ(3,2)
// If gcc_version >= 3.2
# else
// Else
# endif
#else
// If not gcc
#endif
From there, you have your makefile pass the appropriate compiler flags based on the compiler type, version, etc, and you're all set.
You can try using some #pragma. See GCC diagnostic pragmas & GCC function specific pragmas.
Otherwise, develop your GCC plugin or your MELT extension and have it provide a pragma which sets the appropriate variables or compiler state inside GCC (actually cc1plus)
In a C++ project documented with Doxygen, I have marked some functions as \deprecated in the Doxygen comments. Is there any way to use these comments (with Doxygen or another tool) in order to detect that another non-deprecated function is calling a deprecated one ? (The project is pretty big and going through all the classes would take a lot of time).
Thanks
If you are using GCC or clang to compile your code you could manually annotate functions.
__attribute__((__deprecated__))
void dep_fun() { }
Then calling dep_fun anywhere in your code will emit a diagnostic message.
If you placed doxygen's \deprecated annotation consistently you should be able to update the code automatically with tools like sed.
Building on Benjamins answer:
Some useful compiler directives:
#ifdef _MSC_VER
#define DEPRECATED __declspec(deprecated)
#elif defined(__GNUC__) | defined(__clang__)
#define DEPRECATED __attribute__((__deprecated__))
#else
#define DEPRECATED
#endif
//usage:
DEPRECATED void foo(int bar);
(warning: untested under clang and msc, only tested on GNUC.)
I'm interested in creating a macro for eliminating the unused variable warning.
This question describes a way to suppress the unused parameter warning by writing a macro inside the function code:
Universally compiler independent way of implementing an UNUSED macro in C/C++
But I'm interested in a macro that can be used in the function signature:
void callback(int UNUSED(some_useless_stuff)) {}
This is what I dug out using Google (source)
#ifdef UNUSED
#elif defined(__GNUC__)
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
#elif defined(__LCLINT__)
# define UNUSED(x) /*#unused#*/ x
#elif defined(__cplusplus)
# define UNUSED(x)
#else
# define UNUSED(x) x
#endif
Can this be further expanded for other compilers?
Edit: For those who can't understand how tagging works: I want a solution for both C and C++. That is why this question is tagged both C and C++ and that is why a C++ only solution is not acceptable.
The way I do it is like this:
#define UNUSED(x) (void)(x)
void foo(const int i) {
UNUSED(i);
}
I've not had a problem with that in Visual Studio, Intel, gcc and clang.
The other option is to just comment out the parameter:
void foo(const int /*i*/) {
// When we need to use `i` we can just uncomment it.
}
Just one small thing, better using __attribute__((__unused__)) as __attribute__((unused)), because unused could be somewhere defined as macro, personally I had a few issues with this situation.
But the trick I'm using is, which I found more readable is:
#define UNUSED(x) (void)x;
It works however only for the variables, and arguments of the methods, but not for the function itself.
After testing and following the comments, the original version mentioned in the question turned out to be good enough.
Using: #define UNUSED(x) __pragma(warning(suppress:4100)) x (mentioned in comments), might be necessary for compiling C on MSVC, but that's such a weird combination, that I didn't include it in the end.
Across many compilers I have used the following, excluding support for lint.
#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
# define PGM_GNUC_UNUSED __attribute__((__unused__))
#else
# define PGM_GNUC_UNUSED
#endif
Tested compilers: GCC, Clang, EKOPath, Intel C Compiler / Composer XE, MinGW32 on Cygwin / Linux / MSYS, MinGW-w64 on Cygwin / Linux, Sun ONE Studio / Oracle Solaris Studio, Visual Studio 2008 / 2010.
Example usage:
pgm_tsc_init (
PGM_GNUC_UNUSED pgm_error_t** error
)
{
...
}
PGM is the standard prefix for this C based project. GNUC is the convention from GLib for this attribute.
I think one compile warns about __attribute__ in certain circumstances but certainly no error.