Reflection in C++26
After the search into the breadth starts today, the search into the depth: reflection.
Reflection
Reflection is the ability of a program to examine, introspect, and modify its structure and behavior.
Reflection in C++ is more. Here are two statements from the proposal (P2996R5).
“We not only want to observe the structure of the program: We also want to ease generating code that depends on those observations. That combination is sometimes referred to as “reflective metaprogramming”, but within WG21 discussion the term “reflection” has often been used informally to refer to the same general idea.“
“This proposal is not intended to be the end-game as far as reflection and compile-time metaprogramming are concerned. Instead, we expect it will be a useful core around which more powerful features will be added incrementally over time. In particular, we believe that most or all the remaining features explored in P1240R2 and that code injection (along the lines described in [P2237R0]) are desirable directions to pursue.“
History
The history of reflection in C++ is based on template metaprogramming. Template metaprogramming started around 1994, and so does reflection. C++98 got runtime reflection (RTTI) and function template type deduction. The type traits library in C++11 improved C++ capabilities. In C++26, we will probably get general reflection support.
My Strategy
I will base my reflection presentation in C++26 on proposal P2996R and use examples from it.
First, let me jump forward and back from the reflection value to the grammatical elements.
Grammatical Elements <=> Reflection Value
The following program starts in the grammatical domain, jumps to the reflection domain, and back to the grammatical domain.
// forthAndBack.cpp (P2996R5) #include <iostream> #include <cassert> #include <concepts> int main() { constexpr auto r = ^int; typename[:r:] x = 42; // Same as: int x = 42; typename[:^char:] c = '*'; // Same as: char c = '*'; static_assert(std::same_as<decltype(x), int>); static_assert(std::same_as<decltype(c), char>); assert(x == 42); assert(c == '*'); }
^
: Reflection Operator creates a reflection value from its operand (^int
and^char
)[: refl :]
: Splicer creates a grammatical element from a reflection value ([:r:]
and[:^char:]
)- Reflection Value is a representation of program elements as a constant expression
The call ^gramOper
creates a reflection value with an opaque reflection type: std::meta::info
. std::same_as
is a concept.
Modernes C++ Mentoring
Do you want to stay informed: Subscribe.
Jumping between the grammatical and reflection domains makes no sense on its own. Let’s do something meaningful. Let’s analyze the program enumString.cpp
deeper.
Enum <=> String
Enum => String
The following example converts an enum value to a string.
// enumString.cpp #include <iostream> #include <experimental/meta> #include <string> #include <type_traits> template<typename E> requires std::is_enum_v<E> // (1) constexpr std::string enum_to_string(E value) { std::string result = "<unnamed>"; [:expand(std::meta::enumerators_of(^E)):] >> // (2) [&]<auto e>{ if (value == [:e:]) { result = std::meta::identifier_of(e); // (3) } }; return result; } int main() { std::cout << '\n'; enum Color { red, green, blue }; std::cout << "enum_to_string(Color::red): " << enum_to_string(Color::red) << '\n'; // std::cout << "enum_to_string(42): " << enum_to_string(42) << '\n'; std::cout << '\n'; }
Line (1) checks if value
is an enumerator using the type trait std::is_enum.
The expression ^E
in line (2) produces the reflection value. You can ignore the function expand
in the same line. The expansion statements are missing in the current implementation.
The functions std::meta::enumerators_of
and std::meta::enumerators_of
in lines (2 and 3) are metafunctions. The metafunctions can only run at compile time because they are declared as consteval
.
Here are a few of them:
namespace std::meta { consteval auto members_of(info type_class) -> vector<info>; consteval auto bases_of(info type_class) -> vector<info>; consteval auto static_data_members_of(info type_class) -> vector<info>; consteval auto nonstatic_data_members_of(info type_class) -> vector<info>; consteval auto subobjects_of(info type_class) -> vector<info> { auto subobjects = bases_of(type_class); subobjects.append_range(nonstatic_data_members_of(type_class)); return subobjects; } consteval auto enumerators_of(info type_enum) -> vector<info>; }
All return a std::vector
. Reflection offers many metafunctions for queering type information and generating code.
Enum <= String
Applying the reverse steps makes an enum out of string.
template <typename E> requires std::is_enum_v<E> constexpr std::optional<E> string_to_enum(std::string_view name) { template for (constexpr auto e : std::meta::enumerators_of(^E)) { if (name == std::meta::identifier_of(e)) { return [:e:]; } } return std::nullopt; }
What’s next?
Reflection offers many metafunctions. I will apply them in my next post.
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery, Matt Godbolt, and Honey Sukesan.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.
My special thanks to Embarcadero | |
My special thanks to PVS-Studio | |
My special thanks to Tipi.build | |
My special thanks to Take Up Code | |
My special thanks to SHAVEDYAKS |
Modernes C++ GmbH
Modernes C++ Mentoring (English)
Rainer Grimm
Yalovastraße 20
72108 Rottenburg
Mail: schulung@ModernesCpp.de
Mentoring: www.ModernesCpp.org
Modernes C++ Mentoring,