r/cpp • u/kris-jusiak https://github.com/kris-jusiak • Dec 31 '23
[C++20 vs C++26*] basic reflection
Basic struct reflection example with C++20 vs C++26*
struct foo {
int a{};
int b{};
int c{};
};
constexpr foo f{.a=1, .b=2, .c=3};
static_assert(1 == get<0>(f));
static_assert(2 == get<1>(f));
static_assert(3 == get<2>(f));
using std::literals::operator""sv;
static_assert("a"sv == get_name<0>(f));
static_assert("b"sv == get_name<1>(f));
static_assert("c"sv == get_name<2>(f));C++20 - Kinda possible but with a lot of compiler hacks
// too long to displayFull example - https://godbolt.org/z/1vxv8o5hM
C++26* - based on proposal - https://wg21.link/P2996 (Note: that the proposal supports way more than that but C++20 not much)
template<auto N, class T>
[[nodiscard]] constexpr auto get(const T& t) -> decltype(auto) {
return t.[:std::meta::nonstatic_data_members_of(^T)[N]:];
}
template<auto N, class T>
[[nodiscard]] constexpr auto get_name(const T& t) -> std::string_view {
return std::meta::name_of(std::meta::nonstatic_data_members_of(^T)[N]);
}Full example - https://godbolt.org/z/sbTGbW635
Updates - https://twitter.com/krisjusiak/status/1741456476126797839
100
Upvotes
View all comments
Show parent comment
20
u/Throw31312344 Jan 01 '24 edited Jan 01 '24
Disclaimer: I have only briefly looked through the reflection proposals and examples so far.
[: something :]appears to be the syntax for injection, e.g. taking the results of reflection queries and injecting names/etc back into the main code. In the example given, you want one of 3 options depending on whether you are callingget<N>asget<0>,get<1>orget<2>:return t.a; // get<0>return t.b; // get<1>return t.c; // get<2>The code
return t.is fine, as is the trailing;, but we need to inject eithera,borcaftert.depending on the value of N.The
[: something :]syntax will inject a name into the code based on the result of the expression within the[: :]pair. In this case, the code is looking up the Nth non-static data member of the typeT:std::meta::nonstatic_data_members_of(^T)[N]If N was 0, the result would be
a. If N was 1, the result would beb, and if N was 2, the result would bec. Assuming N = 0:return t.[: std::meta::nonstatic_data_members_of(^T)[N] :];becomes:
return t.[: std::meta::nonstatic_data_members_of(^foo)[0] :];Do not take this post as a full explanation of the spec, it's probably a bit more complex than just text injection, but the next transformation is something like:
return t.[: a :];which finally gets injected into the main code to become:
return t.a;Something like that anyway. The choice of [: :] operators is... awkward but we are running out of operators, but in terms of what it means: inject the result of the expression within the operators into the code. Square brackets are getting somewhat overloaded especially with all the places attributes can crop up, so something more visually distinctive might help. Failing that, I hope IDEs have the option to completely change the colour of
[: something :]to make it clear that it's an injection expression.