Reputation: 191
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?
Upvotes: 4
Views: 1363
Reputation: 36637
__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.
Upvotes: 1
Reputation: 191
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:
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).
Upvotes: 2