HunterZ
HunterZ

Reputation: 191

C++ macro to test availability of __func__, __FUNCTION__, __PRETTY_FUNCTION__, etc

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

Answers (2)

Peter
Peter

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

HunterZ
HunterZ

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:

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).

Upvotes: 2

Related Questions