Skip to content

Instantly share code, notes, and snippets.

@schaumb
Last active May 14, 2024 18:58
Show Gist options
  • Save schaumb/d036ab04df35550866577edea51f7f10 to your computer and use it in GitHub Desktop.
Save schaumb/d036ab04df35550866577edea51f7f10 to your computer and use it in GitHub Desktop.
c++ get member name from member pointer
// see at godbolt : https://godbolt.org/z/zMGEGs99v
#include <string_view>
template<auto V>
constexpr static auto n() noexcept {
#ifdef _MSC_VER
std::string_view sv = __FUNCSIG__;
auto to = sv.rfind('>')-1;
for (std::size_t close = sv[to] == ')'; close > 0; ) {
switch(sv[to = sv.find_last_of(")(", to-1)]) {
case ')': ++close; break;
case '(': if (!--close) --to; break;
}
}
for (std::size_t close = sv[to] == '>'; close > 0; ) {
switch(sv[to = sv.find_last_of("><", to-1)]) {
case '>': ++close; break;
case '<': if (!--close) --to; break;
}
}
auto from = sv.find_last_of(">:", to);
return sv.substr(from + 1, to - from);
#else
std::string_view sv = __PRETTY_FUNCTION__;
auto from = sv.rfind(':');
return sv.substr(from + 1, sv.size() - 2 - from);
#endif
}
template<class From, class Type>
From get_base_type(Type From::*);
template<class T>
union union_type {
char c;
T f;
constexpr union_type() : c{} {}
};
template<class T>
constexpr extern T constexpr_static_init {};
template<auto V>
constexpr static std::string_view get_name_if_exists() noexcept {
if constexpr (std::is_member_function_pointer_v<decltype(V)>) {
return n<V>();
} else {
#ifdef _MSC_VER
# if _MSVC_LANG >= 202000
return n<&(constexpr_static_init<
union_type<decltype(get_base_type(V))>
>.f.*V)>();
# else // if_MSVC_LANG < 202000
return "";
# endif // _MSVC_LANG >= 202000
#else // if !defined(_MSC_VER)
return n<V>();
#endif // _MSC_VER
}
}
template<auto N>
constexpr auto get_name(std::string_view name = get_name_if_exists<N>()) {
return name;
}
@Desp4
Copy link

Desp4 commented May 14, 2024

Very neat compiler abuse, I was really out of ideas on how to make MSVC spit out member variable names. However this code does not compile for non-trivial types under c++20 on MSVC.
Currently, I've found a fix via an extra level of union nesting(+ constexpr destructors to make this code c++20 compliant)

Here's the minimal change to the invocation of n in the member variable branch(signature parsing would have to be adjusted)

return n<&(constexpr_static_init<
    union_type<union_type<decltype(get_base_type(V))>>
>.f.f.*V)>();

// and union_type has a constexpr destructor
template<class T>
union union_type {
    char c;
    T f;
    constexpr union_type() : c{} {}
    constexpr ~union_type() {}
};

@schaumb
Copy link
Author

schaumb commented May 14, 2024

Hi @Desp4 ! Thanks for the note. This gist evolved further here, and from this came the idea for a basic reflection here, where the union type is eliminated from the code.

@Desp4
Copy link

Desp4 commented May 14, 2024

I see, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment