Reflexion in C++26: Metafunktionen
Reflection bietet im neuen C++-Standard viele Metafunktionen, die zur Compile-Zeit ausgeführt werden.
- Rainer Grimm
Nach der Einführung zu Reflection in C++26 in meinem vorherigen Beitrag geht es dieses Mal um die Metafunktionen. Diese werden als consteval
deklariert. consteval
erstellt eine sogenannte immediate Funktion. Jeder Aufruf einer sofortigen Funktion erstellt eine Compilezeit-Konstante. Um es direkter auszudrücken: Eine consteval
(immediate ) Funktion wird zur Compile-Zeit ausgeführt.
Mehr über consteval
und constinit
steht in meinem Artikel Zwei neue Schlüsselwörter in C++20: consteval und constinit.
Wie wir bisher wissen:
constexpr auto r = ^int;
typename[:r:] x = 42; // Same as: int x = 42;
typename[:^char:] c = '*'; // Same as: char c = '*';
Der Reflexionsoperator (^
) erzeugt aus dem grammatikalischen Element (C++-Element) einen Reflexionswert. Der Datentyp des Reflexionswerts ist std::meta::info
. Der Reflexionswert (die Reflection) kann ein Argument der Metafunktionen oder der Splicer ([: refl :]
) sein. Die Splicer erzeugen grammatikalische Elemente.
Der Reflexionswert kann also wie folgt definiert werden:
namespace std {
namespace meta {
using info = decltype(^::);
}
}
Was fehlt noch? Natürlich fehlen die Metafunktionen. Ich werde sie aus zwei Gründen alle vorstellen:
- Es gibt viele Metafunktionen.
- Es gibt (Stand Oktober 2024) keine offizielle Auflistung aller Metafunktionen.
Metafunktionen
namespace std::meta {
using info = decltype(^::);
template <typename R>
concept reflection_range = /* see above */;
// name and location
consteval auto identifier_of(info r) -> string_view;
consteval auto u8identifier_of(info r) -> u8string_view;
consteval auto display_string_of(info r) -> string_view;
consteval auto u8display_string_of(info r) -> u8string_view;
consteval auto source_location_of(info r) -> source_location;
// type queries
consteval auto type_of(info r) -> info;
consteval auto parent_of(info r) -> info;
consteval auto dealias(info r) -> info;
// object and value queries
consteval auto object_of(info r) -> info;
consteval auto value_of(info r) -> info;
// template queries
consteval auto template_of(info r) -> info;
consteval auto template_arguments_of(info r) -> vector<info>;
// member queries
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>;
consteval auto enumerators_of(info type_enum) -> vector<info>;
// member access
struct access_context {
static consteval access_context current() noexcept;
consteval access_context() noexcept;
};
consteval auto is_accessible(
info r,
acess_context from = access_context::current());
consteval auto accessible_members_of(
info target,
access_context from = access_context::current()) -> vector<info>;
consteval auto accessible_bases_of(info target,
info target,
access_context from = access_context::current()) -> vector<info>;
consteval auto accessible_nonstatic_data_members_of(
info target,
access_context from = access_context::current()) -> vector<info>;
consteval auto accessible_static_data_members_of(
info target,
access_context from = access_context::current()) -> vector<info>;
consteval auto accessible_subobjects_of(
info target,
access_context from = access_context::current()) -> vector<info>;
// substitute
template <reflection_range R = initializer_list<info>>
consteval auto can_substitute(info templ, R&& args) -> bool;
template <reflection_range R = initializer_list<info>>
consteval auto substitute(info templ, R&& args) -> info;
// reflect_invoke
template <reflection_range R = initializer_list<info>>
consteval auto reflect_invoke(info target, R&& args) -> info;
template <reflection_range R1 = initializer_list<info>, reflection_range R2 = initializer_list<info>>
consteval auto reflect_invoke(info target, R1&& tmpl_args, R2&& args) -> info;
// reflect expression results
template <typename T>
consteval auto reflect_value(T value) -> info;
template <typename T>
consteval auto reflect_object(T& value) -> info;
template <typename T>
consteval auto reflect_function(T& value) -> info;
// extract
template <typename T>
consteval auto extract(info) -> T;
// other type predicates (see the wording)
consteval auto is_public(info r) -> bool;
consteval auto is_protected(info r) -> bool;
consteval auto is_private(info r) -> bool;
consteval auto is_virtual(info r) -> bool;
consteval auto is_pure_virtual(info entity) -> bool;
consteval auto is_override(info entity) -> bool;
consteval auto is_final(info r) -> bool;
consteval auto is_deleted(info entity) -> bool;
consteval auto is_defaulted(info entity) -> bool;
consteval auto is_explicit(info entity) -> bool;
consteval auto is_noexcept(info entity) -> bool;
consteval auto is_bit_field(info entity) -> bool;
consteval auto is_enumerator(info entity) -> bool;
consteval auto is_const(info r) -> bool;
consteval auto is_volatile(info r) -> bool;
consteval auto is_lvalue_reference_qualified(info r) -> bool;
consteval auto is_rvalue_reference_qualified(info r) -> bool;
consteval auto has_static_storage_duration(info r) -> bool;
consteval auto has_thread_storage_duration(info r) -> bool;
consteval auto has_automatic_storage_duration(info r) -> bool;
consteval auto has_internal_linkage(info r) -> bool;
consteval auto has_module_linkage(info r) -> bool;
consteval auto has_external_linkage(info r) -> bool;
consteval auto has_linkage(info r) -> bool;
consteval auto is_class_member(info entity) -> bool;
consteval auto is_namespace_member(info entity) -> bool;
consteval auto is_nonstatic_data_member(info entity) -> bool;
consteval auto is_static_member(info entity) -> bool;
consteval auto is_base(info entity) -> bool;
consteval auto is_data_member_spec(info r) -> bool;
consteval auto is_namespace(info entity) -> bool;
consteval auto is_function(info entity) -> bool;
consteval auto is_variable(info entity) -> bool;
consteval auto is_type(info entity) -> bool;
consteval auto is_type_alias(info entity) -> bool;
consteval auto is_namespace_alias(info entity) -> bool;
consteval auto is_complete_type(info entity) -> bool;
consteval auto is_template(info entity) -> bool;
consteval auto is_function_template(info entity) -> bool;
consteval auto is_variable_template(info entity) -> bool;
consteval auto is_class_template(info entity) -> bool;
consteval auto is_alias_template(info entity) -> bool;
consteval auto is_conversion_function_template(info entity) -> bool;
consteval auto is_operator_function_template(info entity) -> bool;
consteval auto is_literal_operator_template(info entity) -> bool;
consteval auto is_constructor_template(info entity) -> bool;
consteval auto is_concept(info entity) -> bool;
consteval auto is_structured_binding(info entity) -> bool;
consteval auto is_value(info entity) -> bool;
consteval auto is_object(info entity) -> bool;
consteval auto has_template_arguments(info r) -> bool;
consteval auto has_default_member_initializer(info r) -> bool;
consteval auto is_special_member(info r) -> bool;
consteval auto is_conversion_function(info r) -> bool;
consteval auto is_operator_function(info r) -> bool;
consteval auto is_literal_operator(info r) -> bool;
consteval auto is_constructor(info r) -> bool;
consteval auto is_default_constructor(info r) -> bool;
consteval auto is_copy_constructor(info r) -> bool;
consteval auto is_move_constructor(info r) -> bool;
consteval auto is_assignment(info r) -> bool;
consteval auto is_copy_assignment(info r) -> bool;
consteval auto is_move_assignment(info r) -> bool;
consteval auto is_destructor(info r) -> bool;
consteval auto is_user_provided(info r) -> bool;
// define_class
struct data_member_options_t;
consteval auto data_member_spec(info type_class,
data_member_options_t options = {}) -> info;
template <reflection_range R = initializer_list<info>>
consteval auto define_class(info type_class, R&&) -> info;
// define_static_string
consteval auto define_static_string(string_view str) -> const char *;
consteval auto define_static_string(u8string_view str) -> const char8_t *;
// data layout
struct member_offsets {
size_t bytes;
size_t bits;
constexpr auto total_bits() const -> . double quote. double quote. double quote. double quote. size_t;
auto operator<=>(member_offsets const&) const = default;
};
consteval auto offset_of(info entity) -> member_offsets;
consteval auto size_of(info entity) -> size_t;
consteval auto alignment_of(info entity) -> size_t;
consteval auto bit_size_of(info entity) -> size_t;
}
Zunächst einmal erinnern die Metafunktionen vielleicht an die Type-Traits-Metafunktionen. Es gibt jedoch einen großen Unterschied: Die Type-Traits-Metafunktionen werden als constexpr
deklariert, die Reflection-Metafunktionen jedoch als consteval
. Das bedeutet, dass die Type-Traits-Metafunktionen zur Compile-Zeit ausgeführt werden können, die Reflection-Metafunktionen jedoch zur Compile-Zeit ausgeführt werden müssen. Folglich müssen deren Argumente konstante Ausdrücke sein.
Die Reflection Library hat Metafunktionen für folgende Operationen:
- gibt den Namen oder den Speicherort zurück,
- gibt den Reflection-Wert von einem Datentyp, Objekt und Wert, Template und Member zurück,
- gibt den Member-Zugriff zurück,
- gibt die Reflection der Template-Argumente zurück,
- gibt das Ergebnis eines Aufrufs zurück,
- gibt das Ergebnis des ausgewerteten Ausdrucks zurück und
- extrahiert den Wert ähnlich wie die Splicers.
Außerdem verfügt die Reflection Library über mehr als 50 Compile-Zeit-Prädikate. Dabei handelt es sich um aufrufbare Elemente, die zur Compile-Zeit einen bool
zurückgibt
Zusätzlich gibt es Metafunktionen, um eine Klasse zu definieren.
Wie geht es weiter?
In meinem nächsten Artikel werde ich die Metafunktionen anwenden. (rme)