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.