Reputation: 541
Where in GCC is the behavior of C++’s __PRETTY_FUNCTION__
defined? I want to replicate its functionality in Visual Studio.
It’s my understanding that I could replace __PRETTY_FUNCTION__
with either __FUNCTION__
or __FUNCSIG__
,¹ but that, then, I’d lose the ability to compile the code on macOS.
How do include __PRETTY_FUNCTION__
’s functionality in code that I want to be able to compile in, say, both Visual Studio and Xcode.
Upvotes: 1
Views: 1883
Reputation: 3123
I was wondering about this myself, found your post, and did some more digging. Here's some answers and more that I discovered...
__PRETTY_FUNCTION__
defined?A recursive search for __PRETTY_FUNCTION__
within the gcc source code mirrored on github suggested that its behavior is mostly defined through DECL_PRETTY_FUNCTION_P
in gcc/cp/pt.cc. This in turn sees to cp_make_fname_decl
being invoked from gcc/cp/decl.cc. And that builds a declaration for __PRETTY_FUNCTION__
.
Currently - as of my reading of this code and experience using __PRETTY_FUNCTION__
- gcc provides a locally scoped, read only, constant expression, character array type of value.
I.e., by its naming convention, __PRETTY_FUNCTION__
may look like a macro (that's what I'd assumed), but by its current implementation (in gcc), it's instead a locally scoped, character array.
__PRETTY_FUNCTION__
’s functionality in code that I want to be able to compile in, say, both Visual Studio and Xcode?Xcode by default I understand is using an Apple version of clang. Clang is more similar to gcc with respect to this question, than to msvc. So if by Xcode, you mean to use the default compiler, that'd be clang which similarly supports __PRETTY_FUNCTION__
.
With this in mind, I like cscrimage's answer using __FUNCSIG__
for msvc and __PRETTY_FUNCTION__
otherwise:
#ifdef _MSC_VER
#define MY_FUNCTION_MACRO __FUNCSIG__
#else
#define MY_FUNCTION_MACRO __PRETTY_FUNCTION__
#endif
Using the abort sort of code within a function such as the following demonstrates the formats of these values for different compilers:
template <typename T> std::string Name()
{
#if defined(__clang__) || defined(__GNUC__)
return std::string {__PRETTY_FUNCTION__};
#elif defined(_MSC_VER)
return std::string {__FUNCSIG__};
#else
return "not supported by compiler";
#endif
}
template <typename T> struct Other {};
using F = Other<int>;
A statement like:
std::cout << Name<F>() << '\n';
On gcc 12.2 yields:
std::string Name() [with T = Other<int>; std::string = std::__cxx11::basic_string<char>]
And on msvc 19.22 yields:
class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl Name<struct Other<int>>(void)
That's pretty compiler dependent and clang is slightly different from gcc as well.
We could however use something like std::regex_replace
with compiler dependent std::regex
values to parse these into a more similar format between these compilers. I didn't find any documentation from gcc nor msvc that guaranteed what this format will be so I wouldn't expect it to stay the same between all releases of compilers either.
In my case (that got me to dig into this), I was particularly concerned about stringifying types and built on these techniques using the template type parameter as the value I intended to return from the replacement. Here's that code as an example that accounts for differences between msvc, clang, and gcc:
template <typename T>
std::string Name()
{
// Ideally return string unique to the type T...
#if defined(_MSC_VER)
// template <typename T> string Name() { return string{__FUNCSIG__}; }
// enum class Fruit {APPLE, PEAR};
// std::cout << Name<Fruit>() << '\n';
// produces:
// class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl Name<enum Fruit>(void)
return std::regex_replace(__FUNCSIG__, std::regex(".* __cdecl [^<]+<(.*)>\\(void\\)"), "$1");
#elif defined(__clang__)
// template <typename T> string Name() { return string{__PRETTY_FUNCTION__}; }
// enum class Fruit {APPLE, PEAR};
// std::cout << Name<Fruit>() << '\n';
// produces: std::string Name() [T = Fruit]
return std::regex_replace(__PRETTY_FUNCTION__, std::regex(".*T = (.*)\\].*"), "$1");
#elif defined(__GNUC__)
// template <typename T> string Name() { return string{__PRETTY_FUNCTION__}; }
// enum class Fruit {APPLE, PEAR};
// std::cout << Name<Fruit>() << '\n';
// produces: std::string Name() [with T = Fruit; std::string = std::__cxx11::basic_string<char>]
return std::regex_replace(__PRETTY_FUNCTION__, std::regex(".*T = (.*);.*"), "$1");
#else
return {}; // not unique but maybe still helpful at avoiding compiler issues
#endif
}
template <typename T> struct Other {};
using F = Other<int>;
int main()
{
std::cout << Name<F>() << '\n';
}
With that, on gcc 12.2 and clang trunk we get Other<int>
, and on msvc 19.22 we get struct Other<int>
- see the full code at godbolt. This was close enough for my purposes.
Regular expressions for instead returning a commonly formatted function signature looks more involved (depending of course on exactly how common a format one wants) but I don't see why that couldn't be done also.
If you can use boost, you may want to look into BOOST_CURRENT_FUNCTION
or boost::core::demangle
depending on whether you just want code to pick the variable for you to use on your platforms or whether you're more interested in re-using code that's already been written to give you the formatting. And if you have RTTI enabled, you can use something like boost::core::demangle(typeid(T).name())
. I notice however that for functions, this will strip off the function name and just provide a readable function signature for it. It also seems like demangle
used between clang and msvc isn't returning a completely platform independent signature for the function either.
Or, if you can use C++20 or newer and it's the function signature and name you're wanting, std::source_location::function_name
looks interesting. It's implementation defined too however.
Upvotes: 2
Reputation: 395
Define your own macro that refers to __FUNCTION__
or __PRETTY_FUNCTION__
based on your compiler:
#ifdef _MSC_VER
#define MY_FUNCTION_MACRO __FUNCTION__ // or __FUNCSIG__
#else
#define MY_FUNCTION_MACRO __PRETTY_FUNCTION__
#endif
And then use MY_FUNCTION_MACRO
throughout your cross-patform code.
Note that MY_FUNCTION_MACRO
will still produce different strings from between VC++ and GCC/Clang. This is unavoidable and gets you as close as you can get.
For example:
int main()
{
std::cout << MY_FUNCTION_MACRO << std::endl;
}
will produce "main" when using __FUNCTION__
or "int __cdecl main(void)" when using __FUNCSIG__
in VC++, whereas __PRETTY_FUNCTION__
produces "int main()" in GCC/Clang.
Upvotes: 6