3

Suppose I have the following class

namespace example {

class Test {
public:
    int a;
};

};

The fully scoped name of this variable would be "example::Test::a"

My question is, is there some way to get this information at compile-time and have it placed into a sting within the class? For example, I want the result to be equivalent to:

namespace example {

class Test {
public:
    int a;

    static const char* a_name() { return "example::Test::a"; }
};

};

Where the a_name() method is "auto-generated" at compile time.

Thanks!

Patrick Wright
  • 1,401
  • 7
  • 13

1 Answers1

4

The name of a itself is not accessible in any way without using features that are not officially part of C++. If you want to stay standard-compliant, you will have no choice but to manually fill in that part.

Beyond that, if you have access to <cxxabi.h>, you can fill in the rest with typeid() and abi::__cxa_demangle, but at that point, you'll have to start concatenating strings at runtime, which is not great, but it does get you close to where you want to be.

#include <typeinfo>
#include <string>
#include <iostream>
#include <memory>
#include <cxxabi.h>

template<typename T>
std::string member_name(const char* name) {
   int status = 0;
   auto type_name = abi::__cxa_demangle(typeid(T).name(), 0, 0, &status);

   // Feel free to return an error string instead of throwing an exception, obviously...
   if(status != 0) throw std::runtime_error("failed to lookup type name");
   std::unique_ptr<char, void(*)(void*)> cleanup(type_name, std::free);

   return std::string(type_name) + "::a";
}

namespace example {
class Test {
public:
    int a;

    static std::string a_name() { 
      return member_name<Test>("a"); 
    }
};
}

int main() {
  std::cout << example::Test::a_name() << "\n";
}

Edit:

If you are willing to go outside of the bounds of standard C++, then you can use compiler-specific macros and a bit of constexpr juggling with std::string_view to get the member name at compile time.

This is adapted from the solution presented at C++ Get name of type in template (Thanks @HolyBlackCat!)

#include <string_view>
#include <iostream>

namespace detail {

struct Placeholder {
  int member;
};

template<auto T>
constexpr std::string_view raw_member_name() {
  #ifdef _MSC_VER
  return __FUNCSIG__;
  #else
  return __PRETTY_FUNCTION__;
  #endif
}

constexpr std::pair<std::size_t, std::size_t> member_name_offsets() {
  std::string_view raw = raw_member_name<&Placeholder::member>();
  std::string_view lookup = "detail::Placeholder::member";

  auto leading = raw.find(lookup);
  auto trailing = raw.size() - lookup.size();
  return {leading, trailing};
}
}

template<auto T>
constexpr std::string_view member_name() {
    constexpr auto offsets = detail::member_name_offsets();
    std::string_view pretty = detail::raw_member_name<T>();
    return pretty.substr(offsets.first, pretty.size() - offsets.second);
}

namespace example {
class Test {
public:
    int a;

    static constexpr auto a_name = member_name<&Test::a>();
};
}

int main() {
  std::cout << example::Test::a_name << "\n"; // outputs: "example::Test::a"
}

If you absolutely must have a null-terminated string, you'll have to also copy the bytes into a std::array<char, L+1> as well.

  • the user was looking for a compile-time solution, could the function be marked as constexpr? – Alessandro Teruzzi Jun 21 '21 at 17:05
  • @AlessandroTeruzzi I've managed to cobble something together, but it's certainly not something I'd like to see in production code. –  Jun 21 '21 at 18:04
  • @AlessandroTeruzzi Here's the same `__PRETTY_FUNCTION__`-based approach, in a more production-ready form: https://stackoverflow.com/questions/1055452/c-get-name-of-type-in-template/59522794#59522794 – HolyBlackCat Jun 21 '21 at 18:05
  • 1
    @Frank What do you mean? I calculate the offsets. – HolyBlackCat Jun 21 '21 at 18:16
  • @HolyBlackCat oh, I see what's going on in there. Yeah, that should be adaptable to this scenario as well (with a placeholder struct type instead of int). Thanks for the callout. I had missed that part on my quick read through. –  Jun 21 '21 at 18:20