Reflexion in C++26: Metafunktionen

Reflection bietet im neuen C++-Standard viele Metafunktionen, die zur Compile-Zeit ausgeführt werden.​

In Pocket speichern vorlesen Druckansicht
Buchstabenwürfel C++

(Bild: SergioVas/Shutterstock)

Lesezeit: 2 Min.
Von
  • Rainer Grimm
Inhaltsverzeichnis

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.

Modernes C++ – Rainer Grimm

Rainer Grimm ist seit vielen Jahren als Softwarearchitekt, Team- und Schulungsleiter tätig. Er schreibt gerne Artikel zu den Programmiersprachen C++, Python und Haskell, spricht aber auch gerne und häufig auf Fachkonferenzen. Auf seinem Blog Modernes C++ beschäftigt er sich intensiv mit seiner Leidenschaft C++.

Mehr über consteval und constinit steht in meinem Artikel Zwei neue Schlüsselwörter in C++20: consteval und constinit.

 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.
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.

In meinem nächsten Artikel werde ich die Metafunktionen anwenden. (rme)