Keywords: C++ | C Programming | Function Name Identifiers | Compiler Extensions | Debugging Techniques
Abstract: This technical article provides an in-depth comparison of the function name identifiers __PRETTY_FUNCTION__, __FUNCTION__, and __func__ in C/C++ programming. It examines their standardization status, compiler support, and practical usage through detailed code examples. The analysis covers C99 and C++11 standards, GCC and Visual C++ extensions, and the modern C++20 std::source_location feature, offering guidance on selection criteria and best practices for different programming scenarios.
Overview of Function Name Identifiers
In C/C++ programming, obtaining the current function name is a common requirement for debugging and logging purposes. The language standards and compilers provide various mechanisms to achieve this functionality. Among these, __func__, __FUNCTION__, and __PRETTY_FUNCTION__ are the most commonly used identifiers. While these identifiers share overlapping functionality, they exhibit significant differences in standardization, compiler support, and output format.
Standard Identifier: __func__
__func__ is the official identifier introduced in the C99 standard and subsequently adopted in C++11. According to C99 standard §6.4.2.2/1, the compiler implicitly declares at the beginning of each function definition:
static const char __func__[] = "function-name";where function-name is the name of the lexically-enclosing function, representing the unadorned function name. It is important to note that __func__ is not a macro and has no special meaning during preprocessing.
In C++11, __func__ is specified as containing an "implementation-defined string" (C++11 §8.4.1[dcl.fct.def.general]/8), which is somewhat less specific than the C standard specification and may lead to implementation variations across different compilers.
Compiler Extension: __FUNCTION__
__FUNCTION__ is a pre-standard extension supported by major compilers including GCC and Visual C++. Functionally, it is largely equivalent to __func__, returning the simple function name. However, since __FUNCTION__ is not part of any standard, its behavior may vary between compilers.
In practice, it is recommended to prioritize the standardized __func__ and consider using __FUNCTION__ only when working with compilers that lack C99/C++11 support. For instance, early versions of Visual C++ that did not support C99 made __FUNCTION__ a viable alternative for obtaining function names.
Enhanced Extension: __PRETTY_FUNCTION__
__PRETTY_FUNCTION__ is a GCC-specific extension that provides richer function information in C++ environments. Unlike __FUNCTION__ and __func__, which return only the simple function name, __PRETTY_FUNCTION__ returns a "pretty" name containing detailed information such as function signatures, class names, and namespaces.
Consider the following C++ template function example:
namespace N {
class C {
public:
template <class T>
static void f(int i) {
std::cout << "__func__: " << __func__ << std::endl;
std::cout << "__FUNCTION__: " << __FUNCTION__ << std::endl;
std::cout << "__PRETTY_FUNCTION__: " << __PRETTY_FUNCTION__ << std::endl;
}
};
}When calling N::C::f<char>(1), the output would be:
__func__: f
__FUNCTION__: f
__PRETTY_FUNCTION__: static void N::C::f(int) [with T = char]This feature makes __PRETTY_FUNCTION__ particularly valuable when dealing with function overloading, template specialization, and complex class hierarchies.
Compiler Compatibility Considerations
Different compilers exhibit varying levels of support for these identifiers. GCC provides comprehensive support for all three identifiers, while Visual C++ offers a similar extension __FUNCSIG__ that functions similarly to __PRETTY_FUNCTION__ but with some differences.
For projects requiring cross-compiler compatibility, a conditional compilation approach is recommended:
#if defined(__GNUC__) || defined(__clang__)
#define CURRENT_FUNCTION __PRETTY_FUNCTION__
#elif defined(_MSC_VER)
#define CURRENT_FUNCTION __FUNCSIG__
#else
#define CURRENT_FUNCTION __func__
#endifC++20 Modern Feature: std::source_location
C++20 introduces the std::source_location class, providing a more standardized approach to obtaining source code location information, including function names. Its function_name() member function returns an implementation-defined null-terminated byte string corresponding to the function name.
Example usage:
#include <source_location>
void log(const std::string& message,
const std::source_location& location = std::source_location::current()) {
std::cout << "Function: " << location.function_name() << " - " << message << std::endl;
}This feature is particularly suitable for logging and debugging tools, as it automatically captures caller function information.
Selection Strategy and Best Practices
When choosing which function name identifier to use, consider the following factors:
- Standardization: Prioritize the standardized
__func__to ensure code portability. - Information Requirements: For detailed function signature information, use
__PRETTY_FUNCTION__in GCC environments and__FUNCSIG__in Visual C++. - Compiler Compatibility: For projects supporting multiple compilers, employ conditional compilation or feature detection mechanisms.
- C++ Version: In C++20 and later, consider using
std::source_locationas a more modern alternative.
Practical Application Scenarios
These function name identifiers are particularly useful in the following scenarios:
- Debugging Output: Tracking execution paths through complex function call chains.
- Logging: Adding contextual information to log entries for easier problem diagnosis.
- Assertion Messages: Providing more detailed error information when assertions fail.
- Performance Profiling: Identifying hot functions in performance analysis tools.
By appropriately selecting and utilizing these identifiers, developers can significantly enhance code debuggability and maintainability while ensuring compatibility across different compilation environments.