Lucas
Lucas

Reputation: 541

Replicating the behavior of __PRETTY_FUNCTION__ in C++

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.


  1. Apologies if the answer to my question is in the text I’m linking to or in the resources it mentions.

Upvotes: 1

Views: 1883

Answers (2)

Louis Langholtz
Louis Langholtz

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

Where in GCC is the behavior of C++’s __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.

How do I include __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

What To Expect

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.

Normalizing The Values

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.

Some Alternatives

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

cscrimge
cscrimge

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

Related Questions