From d63cea6cbda9864da288e0fb23a28ab7855714a0 Mon Sep 17 00:00:00 2001 From: Daniel Katz Date: Sun, 4 Feb 2024 11:45:31 -0500 Subject: [PATCH 1/3] Add 'reflect_invoke' metafunction and motivating example. --- 2996_reflection/reflection.md | 102 +++++++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/2996_reflection/reflection.md b/2996_reflection/reflection.md index 80ae2cf4..8834d745 100644 --- a/2996_reflection/reflection.md +++ b/2996_reflection/reflection.md @@ -30,7 +30,8 @@ Since [@P2996R1], several changes to the overall library API: * added `qualified_name_of` (to partner with `name_of`) * removed `is_static` for being ambiguous, added `has_internal_linkage` (and `has_linkage` and `has_external_linkage`) and `is_static_member` instead -* added `is_class_member` and `is_namespace_member` +* added `is_class_member`, `is_namespace_member`, and `is_concept` +* added `reflect_invoke` Since [@P2996R0]: @@ -999,6 +1000,86 @@ Thus, the only instantiations of `TU_Ticket::Helper` occur because of the call t [On Compiler Explorer](https://godbolt.org/z/1vEjW4sTr). +## Emulating typeful reflection +Although we believe a single opaque `std::meta::info` type to be the best and most scalable foundation for reflection, we acknowledge the desire expressed by SG7 for future support for "typeful reflection". The following demonstrates one possible means of assembling a typeful reflection library, in which different classes of reflections are represented by distinct types, on top of the facilities proposed here. + +::: bq +```cpp +// Represents a 'std::meta::info' constrained by a predicate. +template + requires (type_of(reflect_invoke(Pred, {reflect_value(^int)})) == ^bool) +struct metatype { + // The underlying 'std::meta::info' value. + const std::meta::info value; + + // Construction is ill-formed unless predicate is satisfied. + consteval metatype(std::meta::info r) : value(r) { + if (![:Pred:](r)) + throw "Reflection is not a member of this metatype"; + } + + // Cast to 'std::meta::info' allows values of this type to be spliced. + consteval operator const std::meta::info&() const { return value; } + + static consteval bool check(std::meta::info r) { return [:Pred:](r); } + + static consteval metatype make(std::meta::info r) { return {r}; } +}; + +// Type representing a "failure to match" any known metatypes. +struct unmatched { static consteval unmatched make(...) { return {}; } }; + +// Returns the given reflection "enriched" with a more descriptive type. +template +constexpr auto enrich = [](std::meta::info r) consteval { + std::array checks = {^Choices::check...}; + std::array ctors = {^Choices::make...}; + + auto choice = ^unmatched; + for (auto [check, make] : std::views::zip(checks, makes)) + if (value_of(reflect_invoke(check, {reflect_value(r)}))) { + choice = make; + break; + } + return reflect_invoke(choice, {reflect_value(r)}); +} +``` +::: + +We can leverage this machinery to select different function overloads based on the "type" of reflection provided as an argument. + +::: bq +```cpp +using type_t = metatype<^std::meta::is_type>; +using fn_t = metatype<^std::meta::is_function>; + +// Example of a function overloaded for different "types" of reflections. +void PrintKind(type_t) { std::println("type"); } +void PrintKind(fn_t) { std::println("function"); } +void PrintKind(unmatched) { std::println("unknown kind"); } + +int main() { + constexpr auto enrich = ::enrich; + + // Demonstration of using 'enrich' to select an overload. + Printer::PrintKind([:enrich(^int):]); // "type" + Printer::PrintKind([:enrich(^main):]); // "function" + Printer::PrintKind([:enrich(^3):]); // "unknown kind" +} +``` + +Note that the `metatype` class can be generalized to wrap values of any literal type, or to wrap multiple values of possibly different types. This has been used, for instance, to select compile-time overloads based on: whether two integers share the same parity, the presence or absence of a value in an `optional`, the type of the value held by a `variant` or an `any`, or the syntactic form of a compile-time string. + +Achieving the same in C++23, with the same generality, would require spelling the argument(s) twice: first to obtain a "classification tag" to use as a template argument, and again to call the function, i.e., + +::: bq +```cpp +Printer::PrintKind(^int). +// or worse... +fn(Arg1, Arg2, Arg3). +``` +::: + # Proposed Features ## The Reflection Operator (`^`) @@ -1409,6 +1490,9 @@ namespace std::meta { template consteval auto reflect_value(T value) -> info; + // @[reflect_invoke](#reflect_invoke)@ + consteval auto reflect_invoke(info target, span args) -> info; + // @[define_class](#data_member_spec-define_class)@ struct data_member_options_t; consteval auto data_member_spec(info class_type, @@ -1659,6 +1743,22 @@ namespace std::meta { This metafunction produces a reflection representing the constant value of the operand. +### `reflect_invoke` + +:::bq +```c++ +namespace std::meta { + consteval auto reflect_invoke(info target, span args) -> info; +} +``` +::: + +This metafunction produces a reflection of the value returned by a call expression. + +Letting `F` be the entity reflected by `target`, and `A_0, ..., A_n` be the sequence of entities reflected by the values held by `args`: if the expression `F(A_0, ..., A_N)` is a well-formed constant expression evaluating to a type that is not `void`, and if every value in `args` is a reflection of a constant value, then `reflect_invoke(target, args)` evaluates to a reflection of the constant value `F(A_0, ..., A_N)`. + +For all other invocations, `reflect_invoke(target, args)` is ill-formed. + ### `data_member_spec`, `define_class` From 1b6c083349dd834570bfa1a5f385ff06f8602658 Mon Sep 17 00:00:00 2001 From: Daniel Katz Date: Mon, 5 Feb 2024 23:19:06 -0500 Subject: [PATCH 2/3] Addressing feedback. --- 2996_reflection/p2996r2.html | 1597 +++++++++++++++++---------------- 2996_reflection/reflection.md | 86 +- 2 files changed, 883 insertions(+), 800 deletions(-) diff --git a/2996_reflection/p2996r2.html b/2996_reflection/p2996r2.html index 2a20ba6e..ca2a1a20 100644 --- a/2996_reflection/p2996r2.html +++ b/2996_reflection/p2996r2.html @@ -593,6 +593,7 @@

Contents

  • 3.13 Implementing member-wise hash_append
  • 3.14 Converting a Struct to a Tuple
  • 3.15 Compile-Time Ticket Counter
  • +
  • 3.16 Emulating typeful reflection
  • 4 Proposed Features
  • 5 Proposed Wording @@ -677,7 +679,8 @@

    qualified_name_of (to partner with name_of)

  • removed is_static for being ambiguous, added has_internal_linkage (and has_linkage and has_external_linkage) and is_static_member instead
  • -
  • added is_class_member and is_namespace_member
  • +
  • added is_class_member, is_namespace_member, and is_concept
  • +
  • added reflect_invoke
  • Since [P2996R0]:

      @@ -1442,6 +1445,74 @@

      substitute returns a specialization of a template, but doesn’t trigger the instantiation of that specialization. Thus, the only instantiations of TU_Ticket::Helper occur because of the call to nonstatic_data_members_of (which is a singleton representing the lone value member).

      On Compiler Explorer.

      +

      3.16 Emulating typeful reflection

      +

      Although we believe a single opaque std::meta::info type to be the best and most scalable foundation for reflection, we acknowledge the desire expressed by SG7 for future support for “typeful reflection”. The following demonstrates one possible means of assembling a typeful reflection library, in which different classes of reflections are represented by distinct types, on top of the facilities proposed here.

      +
      +
      // Represents a 'std::meta::info' constrained by a predicate.
      +template <std::meta::info Pred>
      +  requires (type_of(^([:Pred:](^int))) == ^bool)
      +struct metatype {
      +  std::meta::info value;
      +
      +  // Construction is ill-formed unless predicate is satisfied.
      +  consteval metatype(std::meta::info r) : value(r) {
      +    if (![:Pred:](r))
      +      throw "Reflection is not a member of this metatype";
      +  }
      +
      +  // Cast to 'std::meta::info' allows values of this type to be spliced.
      +  consteval operator std::meta::info() const { return value; }
      +
      +  static consteval bool check(std::meta::info r) { return [:Pred:](r); }
      +};
      +
      +// Type representing a "failure to match" any known metatypes.
      +struct unmatched {
      +  consteval unmatched(std::meta::info) {}
      +  static consteval bool check(std::meta::info) { return true; }
      +};
      +
      +// Returns the given reflection "enriched" with a more descriptive type.
      +template <typename... Choices>
      +consteval std::meta::info enrich(std::meta::info r) {
      +  std::array ctors = {members_of(^Choices, std::meta::is_constructor)[0]...,
      +                      members_of(^unmatched, std::meta::is_constructor)[0]};
      +  std::array checks = {^Choices::check..., ^unmatched::check};
      +
      +  std::meta::info choice;
      +  for (auto [check, ctor] : std::views::zip(checks, ctors))
      +    if (value_of<bool>(reflect_invoke(check, {reflect_value(r)})))
      +      return reflect_invoke(ctor, {reflect_value(r)});
      +
      +  std::unreachable();
      +}
      +
      +

      We can leverage this machinery to select different function overloads based on the “type” of reflection provided as an argument.

      +
      +
      using type_t = metatype<^std::meta::is_type>;
      +using fn_t = metatype<^std::meta::is_function>;
      +
      +// Example of a function overloaded for different "types" of reflections.
      +void PrintKind(type_t) { std::println("type"); }
      +void PrintKind(fn_t) { std::println("function"); }
      +void PrintKind(unmatched) { std::println("unknown kind"); }
      +
      +int main() {
      +  // Classifies any reflection as one of: Type, Function, or Unmatched.
      +  auto enrich = [](std::meta::info r) { return ::enrich<type_t, fn_t>(r); };
      +
      +  // Demonstration of using 'enrich' to select an overload.
      +  PrintKind([:enrich(^main):]);  // "function"
      +  PrintKind([:enrich(^int):]);   // "type"
      +  PrintKind([:enrich(^3):]);     // "unknown kind"
      +
      +

      Note that the metatype class can be generalized to wrap values of any literal type, or to wrap multiple values of possibly different types. This has been used, for instance, to select compile-time overloads based on: whether two integers share the same parity, the presence or absence of a value in an optional, the type of the value held by a variant or an any, or the syntactic form of a compile-time string.

      +

      Achieving the same in C++23, with the same generality, would require spelling the argument(s) twice: first to obtain a “classification tag” to use as a template argument, and again to call the function, i.e.,

      +
      +
      Printer::PrintKind<classify(^int)>(^int).
      +// or worse...
      +fn<classify(Arg1, Arg2, Arg3)>(Arg1, Arg2, Arg3).
      +

      4 Proposed Features

      4.1 The Reflection Operator (^)

      The reflection operator produces a reflection value from a grammatical construct (its operand):

      @@ -1480,7 +1551,7 @@

      4.2The operand of a splicer is implicitly converted to a std::meta::info prvalue (i.e., if the operand expression has a class type that with a conversion function to convert to std::meta::info, splicing can still work).

      Attempting to splice a reflection value that does not meet the requirement of the splice is ill-formed. For example:

      -
      typename[: ^:: :] x = 0;  // Error.
      +
      typename[: ^:: :] x = 0;  // Error.

      4.2.1 Range Splicers

      The splicers described above all take a single object of type std::meta::info (described in more detail below). However, there are many cases where we don’t have a single reflection, we have a range of reflections - and we want to splice them all in one go. For that, we need a different form of splicer: a range splicer.

      @@ -1498,46 +1569,46 @@

      -
      template <typename T>
      -constexpr auto struct_to_tuple(T const& t) {
      -  constexpr auto members = nonstatic_data_members_of(^T);
      -
      -  constexpr auto indices = []{
      -    std::array<int, members.size()> indices;
      -    std::ranges::iota(indices, 0);
      -    return indices;
      -  }();
      -
      -  constexpr auto [...Is] = indices;
      -  return std::make_tuple(t.[: members[Is] :]...);
      -}
      -
      template <typename T>
      -constexpr auto struct_to_tuple(T const& t) {
      -  constexpr auto members = nonstatic_data_members_of(^T);
      -  return std::make_tuple(t.[: ...members :]...);
      -}
      +
      template <typename T>
      +constexpr auto struct_to_tuple(T const& t) {
      +  constexpr auto members = nonstatic_data_members_of(^T);
      +
      +  constexpr auto indices = []{
      +    std::array<int, members.size()> indices;
      +    std::ranges::iota(indices, 0);
      +    return indices;
      +  }();
      +
      +  constexpr auto [...Is] = indices;
      +  return std::make_tuple(t.[: members[Is] :]...);
      +}
      +
      template <typename T>
      +constexpr auto struct_to_tuple(T const& t) {
      +  constexpr auto members = nonstatic_data_members_of(^T);
      +  return std::make_tuple(t.[: ...members :]...);
      +}

      A range splice, [: ... r :], would accept as its argument a constant range of meta::info, r, and would behave as an unexpanded pack of splices. So the above expression

      -
      make_tuple(t.[: ... members :]...)
      +
      make_tuple(t.[: ... members :]...)

      would evaluate as

      -
      make_tuple(t.[:members[0]:], t.[:members[1]:], ..., t.[:members[N-1]:])
      +
      make_tuple(t.[:members[0]:], t.[:members[1]:], ..., t.[:members[N-1]:])

      This is a very useful facility indeed!

      However, range splicing of dependent arguments is at least an order of magnitude harder to implement than ordinary splicing. We think that not including range splicing gives us a better chance of having reflection in C++26. Especially since, as this paper’s examples demonstrate, a lot can be done without them.

      Another way to work around a lack of range splicing would be to implement with_size<N>(f), which would behave like f(integral_constant<size_t, 0>{}, integral_constant<size_t, 0>{}, ..., integral_constant<size_t, N-1>{}). Which is enough for a tolerable implementation:

      -
      template <typename T>
      -constexpr auto struct_to_tuple(T const& t) {
      -  constexpr auto members = nonstatic_data_members_of(^T);
      -  return with_size<members.size()>([&](auto... Is){
      -    return std::make_tuple(t.[: members[Is] :]...);
      -  });
      -}
      +
      template <typename T>
      +constexpr auto struct_to_tuple(T const& t) {
      +  constexpr auto members = nonstatic_data_members_of(^T);
      +  return with_size<members.size()>([&](auto... Is){
      +    return std::make_tuple(t.[: members[Is] :]...);
      +  });
      +}

      (P1240 did propose range splicers.)

      4.2.2 Syntax discussion

      @@ -1545,17 +1616,17 @@

      [: and :] be single tokens rather than combinations of [, ], and :. Among others, it simplifies the handling of expressions like arr[[:refl():]]. On the flip side, it requires a special rule like the one that was made to handle <:: to leave the meaning of arr[::N] unchanged and another one to avoid breaking a (somewhat useless) attribute specifier of the form [[using ns:]].

      A syntax that is delimited on the left and right is useful here because spliced expressions may involve lower-precedence operators. However, there are other possibilities. For example, now that $ is available in the basic source character set, we might consider $<expr>. This is somewhat natural to those of us that have used systems where $ is used to expand placeholders in document templates. For example:

      -
      $select_type(3) *ptr = nullptr;
      +
      $select_type(3) *ptr = nullptr;

      The prefixes typename and template are only strictly needed in some cases where the operand of the splice is a dependent expression. In our proposal, however, we only make typename optional in the same contexts where it would be optional for qualified names with dependent name qualifiers. That has the advantage to catch unfortunate errors while keeping a single rule and helping human readers parse the intended meaning of otherwise ambiguous constructs.

      4.3 std::meta::info

      The type std::meta::info can be defined as follows:

      -
      namespace std {
      -  namespace meta {
      -    using info = decltype(^int);
      -  }
      -}
      +
      namespace std {
      +  namespace meta {
      +    using info = decltype(^int);
      +  }
      +}

      In our initial proposal a value of type std::meta::info can represent:

        @@ -1569,31 +1640,31 @@

        4.3

        Notably absent at this time are general non-constant expressions (that aren’t expression-ids referring to functions, variables or structured bindings). For example:

        -
        int x = 0;
        -void g() {
        -  [:^x:] = 42;     // Okay.  Same as: x = 42;
        -  x = [:^(2*x):];  // Error: "2*x" is a general non-constant expression.
        -  constexpr int N = 42;
        -  x = [:^(2*N):];  // Okay: "2*N" is a constant-expression.
        -}
        +
        int x = 0;
        +void g() {
        +  [:^x:] = 42;     // Okay.  Same as: x = 42;
        +  x = [:^(2*x):];  // Error: "2*x" is a general non-constant expression.
        +  constexpr int N = 42;
        +  x = [:^(2*N):];  // Okay: "2*N" is a constant-expression.
        +}

        Note that for ^(2*N) an implementation only has to capture the constant value of 2*N and not various other properties of the underlying expression (such as any temporaries it involves, etc.).

        The type std::meta::info is a scalar type. Nontype template arguments of type std::meta::info are permitted. The entity being reflected can affect the linkage of a template instance involving a reflection. For example:

        -
        template<auto R> struct S {};
        -
        -extern int x;
        -static int y;
        -
        -S<^x> sx;  // S<^x> has external name linkage.
        -S<^y> sy;  // S<^y> has internal name linkage.
        +
        template<auto R> struct S {};
        +
        +extern int x;
        +static int y;
        +
        +S<^x> sx;  // S<^x> has external name linkage.
        +S<^y> sy;  // S<^y> has internal name linkage.

        Namespace std::meta is associated with type std::meta::info: That allows the core meta functions to be invoked without explicit qualification. For example:

        -
        #include <meta>
        -struct S {};
        -std::string name2 = std::meta::name_of(^S);  // Okay.
        -std::string name1 = name_of(^S);             // Also okay.
        +
        #include <meta>
        +struct S {};
        +std::string name2 = std::meta::name_of(^S);  // Okay.
        +std::string name1 = name_of(^S);             // Also okay.

        4.4 Metafunctions

        We propose a number of metafunctions declared in namespace std::meta to operator on reflection values. Adding metafunctions to an implementation is expected to be relatively “easy” compared to implementing the core language features described previously. However, despite offering a normal consteval C++ function interface, each on of these relies on “compiler magic” to a significant extent.

        @@ -1621,9 +1692,9 @@

        std::expected and exceptions here:

        -
        template <typename T>
        -  requires (template_of(^T) == ^std::optional)
        -void foo();
        +
        template <typename T>
        +  requires (template_of(^T) == ^std::optional)
        +void foo();

        If template_of returns an excepted<info, E>, then foo<int> is a substitution failure — expected<T, E> is equality-comparable to T, that comparison would evaluate to false but still be a constant expression.

        If template_of returns info but throws an exception, then foo<int> would cause that exception to be uncaught, which would make the comparison not a constant expression. This actually makes the constraint ill-formed - not a substitution failure. In order to have foo<int> be a substitution failure, either the constraint would have to first check that T is a template or we would have to change the language rule that requires constraints to be constant expressions (we would of course still keep the requirement that the constraint is a bool).

        @@ -1632,14 +1703,14 @@

        4.4.2 Handling Aliases

        Consider

        -
        using A = int;
        +
        using A = int;

        In C++ today, A and int can be used interchangeably and there is no distinction between the two types. With reflection as proposed in this paper, that will no longer be the case. ^A yields a reflection of an alias to int, while ^int yields a reflection of int. ^A == ^int evaluates to false, but there will be a way to strip aliases - so dealias(^A) == ^int evaluates to true.

        This opens up the question of how various other metafunctions handle aliases and it is worth going over a few examples:

        -
        using A = int;
        -using B = std::unique_ptr<int>;
        -template <class T> using C = std::unique_ptr<T>;
        +
        using A = int;
        +using B = std::unique_ptr<int>;
        +template <class T> using C = std::unique_ptr<T>;

        This paper is proposing that:

          @@ -1650,204 +1721,216 @@

          4.4.3 Synopsis

          Here is a synopsis for the proposed library API. The functions will be explained below.

          -
          namespace std::meta {
          -  // name and location
          -  consteval auto name_of(info r) -> string_view;
          -  consteval auto qualified_name_of(info r) -> string_view;
          -  consteval auto display_name_of(info r) -> string_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;
          -
          -  // template queries
          -  consteval auto template_of(info r) -> info;
          -  consteval auto template_arguments_of(info r) -> vector<info>;
          -
          -  // member queries
          -  template<typename ...Fs>
          -    consteval auto members_of(info class_type, Fs ...filters) -> vector<info>;
          -  template<typename ...Fs>
          -    consteval auto bases_of(info class_type, Fs ...filters) -> vector<info>;
          -  consteval auto static_data_members_of(info class_type) -> vector<info>;
          -  consteval auto nonstatic_data_members_of(info class_type) -> vector<info>;
          -  consteval auto subobjects_of(info class_type) -> vector<info>;
          -  consteval auto enumerators_of(info enum_type) -> vector<info>;
          -
          -  // substitute
          -  consteval auto substitute(info templ, span<info const> args) -> info;
          -
          -   // value_of
          -  template<typename T>
          -    consteval auto value_of(info) -> T;
          -
          -  // test_type
          -  consteval auto test_type(info templ, info type) -> bool;
          -  consteval auto test_types(info templ, span<info const> types) -> bool;
          -
          -  // 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_accessible(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_deleted(info entity) -> bool;
          -  consteval auto is_defaulted(info entity) -> bool;
          -  consteval auto is_explicit(info entity) -> bool;
          -  consteval auto is_bit_field(info entity) -> bool;
          -  consteval auto has_static_storage_duration(info r) -> bool;
          -  consteval auto has_internal_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_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_alias(info entity) -> bool;
          -  consteval auto is_incomplete_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_concept(info entity) -> bool;
          -  consteval auto has_template_arguments(info r) -> bool;
          -  consteval auto is_constructor(info r) -> bool;
          -  consteval auto is_destructor(info r) -> bool;
          -  consteval auto is_special_member(info r) -> bool;
          -
          -  // reflect_value
          -  template<typename T>
          -    consteval auto reflect_value(T value) -> info;
          -
          -  // define_class
          -  struct data_member_options_t;
          -  consteval auto data_member_spec(info class_type,
          -                                  data_member_options_t options = {}) -> info;
          -  consteval auto define_class(info class_type, span<info const>) -> info;
          -
          -  // data layout
          -  consteval auto offset_of(info entity) -> size_t;
          -  consteval auto size_of(info entity) -> size_t;
          -  consteval auto bit_offset_of(info entity) -> size_t;
          -  consteval auto bit_size_of(info entity) -> size_t;
          -  consteval auto alignment_of(info entity) -> size_t;
          -}
          +
          namespace std::meta {
          +  // name and location
          +  consteval auto name_of(info r) -> string_view;
          +  consteval auto qualified_name_of(info r) -> string_view;
          +  consteval auto display_name_of(info r) -> string_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;
          +
          +  // template queries
          +  consteval auto template_of(info r) -> info;
          +  consteval auto template_arguments_of(info r) -> vector<info>;
          +
          +  // member queries
          +  template<typename ...Fs>
          +    consteval auto members_of(info class_type, Fs ...filters) -> vector<info>;
          +  template<typename ...Fs>
          +    consteval auto bases_of(info class_type, Fs ...filters) -> vector<info>;
          +  consteval auto static_data_members_of(info class_type) -> vector<info>;
          +  consteval auto nonstatic_data_members_of(info class_type) -> vector<info>;
          +  consteval auto subobjects_of(info class_type) -> vector<info>;
          +  consteval auto enumerators_of(info enum_type) -> vector<info>;
          +
          +  // substitute
          +  consteval auto substitute(info templ, span<info const> args) -> info;
          +
          +  // reflect_invoke
          +  consteval auto reflect_invoke(info target, span<info const> args) -> info;
          +
          +   // value_of
          +  template<typename T>
          +    consteval auto value_of(info) -> T;
          +
          +  // test_type
          +  consteval auto test_type(info templ, info type) -> bool;
          +  consteval auto test_types(info templ, span<info const> types) -> bool;
          +
          +  // 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_accessible(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_deleted(info entity) -> bool;
          +  consteval auto is_defaulted(info entity) -> bool;
          +  consteval auto is_explicit(info entity) -> bool;
          +  consteval auto is_bit_field(info entity) -> bool;
          +  consteval auto has_static_storage_duration(info r) -> bool;
          +  consteval auto has_internal_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_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_alias(info entity) -> bool;
          +  consteval auto is_incomplete_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_concept(info entity) -> bool;
          +  consteval auto has_template_arguments(info r) -> bool;
          +  consteval auto is_constructor(info r) -> bool;
          +  consteval auto is_destructor(info r) -> bool;
          +  consteval auto is_special_member(info r) -> bool;
          +
          +  // reflect_value
          +  template<typename T>
          +    consteval auto reflect_value(T value) -> info;
          +
          +  // define_class
          +  struct data_member_options_t;
          +  consteval auto data_member_spec(info class_type,
          +                                  data_member_options_t options = {}) -> info;
          +  consteval auto define_class(info class_type, span<info const>) -> info;
          +
          +  // data layout
          +  consteval auto offset_of(info entity) -> size_t;
          +  consteval auto size_of(info entity) -> size_t;
          +  consteval auto bit_offset_of(info entity) -> size_t;
          +  consteval auto bit_size_of(info entity) -> size_t;
          +  consteval auto alignment_of(info entity) -> size_t;
          +}

          4.4.4 name_of, display_name_of, source_location_of

          -
          namespace std::meta {
          -  consteval auto name_of(info r) -> string_view;
          -  consteval auto qualified_name_of(info r) -> string_view;
          -  consteval auto display_name_of(info r) -> string_view;
          -  consteval auto source_location_of(info r) -> source_location;
          -}
          +
          namespace std::meta {
          +  consteval auto name_of(info r) -> string_view;
          +  consteval auto qualified_name_of(info r) -> string_view;
          +  consteval auto display_name_of(info r) -> string_view;
          +  consteval auto source_location_of(info r) -> source_location;
          +}

          Given a reflection r that designates a declared entity X, name_of(r) and qualified_name_of(r) return a string_view holding the unqualified and qualified name of X, respectively. For all other reflections, an empty string_view is produced. For template instances, the name does not include the template argument list. The contents of the string_view consist of characters of the basic source character set only (an implementation can map other characters using universal character names).

          Given a reflection r, display_name_of(r) returns a unspecified non-empty string_view. Implementations are encouraged to produce text that is helpful in identifying the reflected construct.

          Given a reflection r, source_location_of(r) returns an unspecified source_location. Implementations are encouraged to produce the correct source location of the item designated by the reflection.

          4.4.5 type_of, parent_of, dealias

          -
          namespace std::meta {
          -  consteval auto type_of(info r) -> info;
          -  consteval auto parent_of(info r) -> info;
          -  consteval auto dealias(info r) -> info;
          -}
          +
          namespace std::meta {
          +  consteval auto type_of(info r) -> info;
          +  consteval auto parent_of(info r) -> info;
          +  consteval auto dealias(info r) -> info;
          +}

          If r is a reflection designating a typed entity, type_of(r) is a reflection designating its type. If r is already a type, type_of(r) is not a constant expression. This can be used to implement the C typeof feature (which works on both types and expressions and strips qualifiers):

          -
          consteval auto do_typeof(std::meta::info r) -> std::meta::info {
          -  return remove_cvref(is_type(r) ? r : type_of(r));
          -}
          -
          -#define typeof(e) [: do_typeof(^e) :]
          +
          consteval auto do_typeof(std::meta::info r) -> std::meta::info {
          +  return remove_cvref(is_type(r) ? r : type_of(r));
          +}
          +
          +#define typeof(e) [: do_typeof(^e) :]

          If r designates a member of a class or namespace, parent_of(r) is a reflection designating its immediately enclosing class or namespace.

          If r designates an alias, dealias(r) designates the underlying entity. Otherwise, dealias(r) produces r. dealias is recursive - it strips all aliases:

          -
          using X = int;
          -using Y = X;
          -static_assert(dealias(^int) == ^int);
          -static_assert(dealias(^X) == ^int);
          -static_assert(dealias(^Y) == ^int);
          +
          using X = int;
          +using Y = X;
          +static_assert(dealias(^int) == ^int);
          +static_assert(dealias(^X) == ^int);
          +static_assert(dealias(^Y) == ^int);

          4.4.6 template_of, template_arguments_of

          -
          namespace std::meta {
          -  consteval auto template_of(info r) -> info;
          -  consteval auto template_arguments_of(info r) -> vector<info>;
          -}
          +
          namespace std::meta {
          +  consteval auto template_of(info r) -> info;
          +  consteval auto template_arguments_of(info r) -> vector<info>;
          +}

          If r is a reflection designated a type that is a specialization of some template, then template_of(r) is a reflection of that template and template_arguments_of(r) is a vector of the reflections of the template arguments. In other words, the preconditions on both is that has_template_arguments(r) is true.

          For example:

          -
          std::vector<int> v = {1, 2, 3};
          -static_assert(template_of(type_of(^v)) == ^std::vector);
          -static_assert(template_arguments_of(type_of(^v))[0] == ^int);
          +
          std::vector<int> v = {1, 2, 3};
          +static_assert(template_of(type_of(^v)) == ^std::vector);
          +static_assert(template_arguments_of(type_of(^v))[0] == ^int);

          4.4.7 members_of, static_data_members_of, nonstatic_data_members_of, bases_of, enumerators_of, subobjects_of

          -
          namespace std::meta {
          -  template<typename ...Fs>
          -    consteval auto members_of(info class_type, Fs ...filters) -> vector<info>;
          -
          -  template<typename ...Fs>
          -    consteval auto bases_of(info class_type, Fs ...filters) -> vector<info>;
          -
          -  consteval auto static_data_members_of(info class_type) -> vector<info> {
          -    return members_of(class_type, is_variable);
          -  }
          -
          -  consteval auto nonstatic_data_members_of(info class_type) -> vector<info> {
          -    return members_of(class_type, is_nonstatic_data_member);
          -  }
          -
          -  consteval auto subobjects_of(info class_type) -> vector<info> {
          -    auto subobjects = bases_of(class_type);
          -    subobjects.append_range(nonstatic_data_members_of(class_type));
          -    return subobjects;
          -  }
          -
          -  consteval auto enumerators_of(info enum_type) -> vector<info>;
          -}
          +
          namespace std::meta {
          +  template<typename ...Fs>
          +    consteval auto members_of(info class_type, Fs ...filters) -> vector<info>;
          +
          +  template<typename ...Fs>
          +    consteval auto bases_of(info class_type, Fs ...filters) -> vector<info>;
          +
          +  consteval auto static_data_members_of(info class_type) -> vector<info> {
          +    return members_of(class_type, is_variable);
          +  }
          +
          +  consteval auto nonstatic_data_members_of(info class_type) -> vector<info> {
          +    return members_of(class_type, is_nonstatic_data_member);
          +  }
          +
          +  consteval auto subobjects_of(info class_type) -> vector<info> {
          +    auto subobjects = bases_of(class_type);
          +    subobjects.append_range(nonstatic_data_members_of(class_type));
          +    return subobjects;
          +  }
          +
          +  consteval auto enumerators_of(info enum_type) -> vector<info>;
          +}

          The template members_of returns a vector of reflections representing the direct members of the class type represented by its first argument. Any nonstatic data members appear in declaration order within that vector. Anonymous unions appear as a nonstatic data member of corresponding union type. If any Filters... argument is specified, a member is dropped from the result if any filter applied to that members reflection returns false. E.g., members_of(^C, std::meta::is_type) will only return types nested in the definition of C and members_of(^C, std::meta::is_type, std::meta::is_variable) will return an empty vector since a member cannot be both a type and a variable.

          The template bases_of returns the direct base classes of the class type represented by its first argument, in declaration order.

          enumerators_of returns the enumerator constants of the indicated enumeration type in declaration order.

          4.4.8 substitute

          -
          namespace std::meta {
          -  consteval auto substitute(info templ, span<info const> args) -> info;
          -}
          +
          namespace std::meta {
          +  consteval auto substitute(info templ, span<info const> args) -> info;
          +}

          Given a reflection for a template and reflections for template arguments that match that template, substitute returns a reflection for the entity obtained by substituting the given arguments in the template. If the template is a concept template, the result is a reflection of a constant of type bool.

          For example:

          -
          constexpr auto r = substitute(^std::vector, std::vector{^int});
          -using T = [:r:]; // Ok, T is std::vector<int>
          +
          constexpr auto r = substitute(^std::vector, std::vector{^int});
          +using T = [:r:]; // Ok, T is std::vector<int>

          This process might kick off instantiations outside the immediate context, which can lead to the program being ill-formed.

          Note that the template is only substituted, not instantiated. For example:

          -
          template<typename T> struct S { typename T::X x; };
          -
          -constexpr auto r = substitute(^S, std::vector{^int});  // Okay.
          -typename[:r:] si;  // Error: T::X is invalid for T = int.
          +
          template<typename T> struct S { typename T::X x; };
          +
          +constexpr auto r = substitute(^S, std::vector{^int});  // Okay.
          +typename[:r:] si;  // Error: T::X is invalid for T = int.
          -

          4.4.9 value_of<T>

          +

          4.4.9 reflect_invoke

          -
          namespace std::meta {
          -  template<typename T> consteval auto value_of(info) -> T;
          -}
          +
          namespace std::meta {
          +  consteval auto reflect_invoke(info target, span<info const> args) -> info;
          +}
          +
          +

          This metafunction produces a reflection of the value returned by a call expression.

          +

          Letting F be the entity reflected by target, and A_0, ..., A_n be the sequence of entities reflected by the values held by args: if the expression F(A_0, ..., A_N) is a well-formed constant expression evaluating to a type that is not void, and if every value in args is a reflection of a constant value, then reflect_invoke(target, args) evaluates to a reflection of the constant value F(A_0, ..., A_N).

          +

          For all other invocations, reflect_invoke(target, args) is ill-formed.

          +

          4.4.10 value_of<T>

          +
          +
          namespace std::meta {
          +  template<typename T> consteval auto value_of(info) -> T;
          +}

          If r is a reflection for a constant-expression or a constant-valued entity of type T, value_of<T>(r) evaluates to that constant value.

          If r is a reflection for a variable of non-reference type T, value_of<T&>(r) and value_of<T const&>(r) are lvalues referring to that variable. If the variable is usable in constant expressions [expr.const], value_of<T>(r) evaluates to its value.

          @@ -1856,89 +1939,89 @@

          4.4.9

          If r is a reflection of a non-bit-field non-reference non-static member of type M in a class C, value_of<M C::*>(r) is the pointer-to-member value for that nonstatic member.

          For other reflection values r, value_of<T>(r) is ill-formed.

          The function template value_of may feel similar to splicers, but unlike splicers it does not require its operand to be a constant-expression itself. Also unlike splicers, it requires knowledge of the type associated with the entity reflected by its operand.

          -

          4.4.10 test_type, test_types

          -
          -
          namespace std::meta {
          -  consteval auto test_type(info templ, info type) -> bool {
          -    return test_types(templ, {type});
          -  }
          -
          -  consteval auto test_types(info templ, span<info const> types) -> bool {
          -    return value_of<bool>(substitute(templ, types));
          -  }
          -}
          +

          4.4.11 test_type, test_types

          +
          +
          namespace std::meta {
          +  consteval auto test_type(info templ, info type) -> bool {
          +    return test_types(templ, {type});
          +  }
          +
          +  consteval auto test_types(info templ, span<info const> types) -> bool {
          +    return value_of<bool>(substitute(templ, types));
          +  }
          +}

          This utility translates existing metaprogramming predicates (expressed as constexpr variable templates or concept templates) to the reflection domain. For example:

          -
          struct S {};
          -static_assert(test_type(^std::is_class_v, ^S));
          +
          struct S {};
          +static_assert(test_type(^std::is_class_v, ^S));

          An implementation is permitted to recognize standard predicate templates and implement test_type without actually instantiating the predicate template. In fact, that is recommended practice.

          -

          4.4.11 reflect_value

          +

          4.4.12 reflect_value

          -
          namespace std::meta {
          -  template<typename T> consteval auto reflect_value(T value) -> info;
          -}
          +
          namespace std::meta {
          +  template<typename T> consteval auto reflect_value(T value) -> info;
          +}

          This metafunction produces a reflection representing the constant value of the operand.

          -

          4.4.12 data_member_spec, define_class

          -
          -
          namespace std::meta {
          -  struct data_member_options_t {
          -    optional<string_view> name;
          -    bool is_static = false;
          -    optional<int> alignment;
          -    optional<int> width;
          -  };
          -  consteval auto data_member_spec(info type,
          -                                  data_member_options_t options = {}) -> info;
          -  consteval auto define_class(info class_type, span<info const>) -> info;
          -}
          +

          4.4.13 data_member_spec, define_class

          +
          +
          namespace std::meta {
          +  struct data_member_options_t {
          +    optional<string_view> name;
          +    bool is_static = false;
          +    optional<int> alignment;
          +    optional<int> width;
          +  };
          +  consteval auto data_member_spec(info type,
          +                                  data_member_options_t options = {}) -> info;
          +  consteval auto define_class(info class_type, span<info const>) -> info;
          +}

          data_member_spec returns a reflection of a description of a data member of given type. Optional alignment, bit-field-width, static-ness, and name can be provided as well. If no name is provided, the name of the data member is unspecified. If is_static is true, the data member is declared static.

          define_class takes the reflection of an incomplete class/struct/union type and a range of reflections of data member descriptions and it completes the given class type with data members as described (in the given order). The given reflection is returned. For now, only data member reflections are supported (via data_member_spec) but the API takes in a range of info anticipating expanding this in the near future.

          For example:

          -
          union U;
          -static_assert(is_type(define_class(^U, {
          -  data_member_spec(^int),
          -  data_member_spec(^char),
          -  data_member_spec(^double),
          -})));
          -
          -// U is now defined to the equivalent of
          -// union U {
          -//   int _0;
          -//   char _1;
          -//   double _2;
          -// };
          -
          -template<typename T> struct S;
          -constexpr auto U = define_class(^S<int>, {
          -  data_member_spec(^int, {.name="i", .align=64}),
          -  data_member_spec(^int, {.name="j", .align=64}),
          -});
          -
          -// S<int> is now defined to the equivalent of
          -// template<> struct S<int> {
          -//   alignas(64) int i;
          -//   alignas(64) int j;
          -// };
          +
          union U;
          +static_assert(is_type(define_class(^U, {
          +  data_member_spec(^int),
          +  data_member_spec(^char),
          +  data_member_spec(^double),
          +})));
          +
          +// U is now defined to the equivalent of
          +// union U {
          +//   int _0;
          +//   char _1;
          +//   double _2;
          +// };
          +
          +template<typename T> struct S;
          +constexpr auto U = define_class(^S<int>, {
          +  data_member_spec(^int, {.name="i", .align=64}),
          +  data_member_spec(^int, {.name="j", .align=64}),
          +});
          +
          +// S<int> is now defined to the equivalent of
          +// template<> struct S<int> {
          +//   alignas(64) int i;
          +//   alignas(64) int j;
          +// };

          When defining a union, if one of the alternatives has a non-trivial destructor, the defined union will still have a destructor provided - that simply does nothing. This allows implementing variant without having to further extend support in define_class for member functions.

          -

          4.4.13 Data Layout Reflection

          -
          -
          namespace std::meta {
          -  consteval auto offset_of(info entity) -> size_t;
          -  consteval auto size_of(info entity) -> size_t;
          -
          -  consteval auto bit_offset_of(info entity) -> size_t;
          -  consteval auto bit_size_of(info entity) -> size_t;
          -
          -  consteval auto alignment_of(info entity) -> size_t;
          -}
          -
          -

          4.4.14 Other Type Traits

          +

          4.4.14 Data Layout Reflection

          +
          +
          namespace std::meta {
          +  consteval auto offset_of(info entity) -> size_t;
          +  consteval auto size_of(info entity) -> size_t;
          +
          +  consteval auto bit_offset_of(info entity) -> size_t;
          +  consteval auto bit_size_of(info entity) -> size_t;
          +
          +  consteval auto alignment_of(info entity) -> size_t;
          +}
          +
          +

          4.4.15 Other Type Traits

          There is a question of whether all the type traits should be provided in std::meta. For instance, a few examples in this paper use std::meta::remove_cvref(t) as if that exists. Technically, the functionality isn’t strictly necessary - since it can be provided indirectly:

          @@ -1953,13 +2036,13 @@

          -

          - + + - - + +
          std::meta::remove_cvref(type)
          std::meta::substitute(^std::remove_cvref_t, {type})
          std::meta::remove_cvref(type)
          std::meta::substitute(^std::remove_cvref_t, {type})
          std::meta::is_const(type)
          std::meta::value_of<bool>(std::meta::substitute(^std::is_const_v, {type}))
          -std::meta::test_type(^std::is_const_v, type)
          std::meta::is_const(type)
          std::meta::value_of<bool>(std::meta::substitute(^std::is_const_v, {type}))
          +std::meta::test_type(^std::is_const_v, type)
          @@ -1985,16 +2068,16 @@

          5.1.3 [lex.operators] Operators and punctuators

          Change the grammar for operator-or-punctuator in paragraph 1 of 5.12 [lex.operators] to include splicer delimiters:

          -
            operator-or-punctuator: one of
          -         {        }        [        ]        (        )        [:        :]
          -         <:       :>       <%       %>       ;        :        ...
          -         ?        ::       .       .*        ->       ->*      ~
          -         !        +        -        *        /        %        ^        &        |
          -         =        +=       -=       *=       /=       %=       ^=       &=       |=
          -         ==       !=       <        >        <=       >=       <=>      &&       ||
          -         <<       >>       <<=      >>=      ++       --       ,
          -         and      or       xor      not      bitand   bitor    compl
          -         and_eq   or_eq    xor_eq   not_eq
          +
            operator-or-punctuator: one of
          +         {        }        [        ]        (        )        [:        :]
          +         <:       :>       <%       %>       ;        :        ...
          +         ?        ::       .       .*        ->       ->*      ~
          +         !        +        -        *        /        %        ^        &        |
          +         =        +=       -=       *=       /=       %=       ^=       &=       |=
          +         ==       !=       <        >        <=       >=       <=>      &&       ||
          +         <<       >>       <<=      >>=      ++       --       ,
          +         and      or       xor      not      bitand   bitor    compl
          +         and_eq   or_eq    xor_eq   not_eq

          5.1.4 [basic.types.general]

          Change the first sentence in paragraph 9 of 6.8.1 [basic.types.general] as follows:

          @@ -2035,16 +2118,16 @@

          primary-expression in 7.5 [expr.prim] as follows:

          -
            primary-expression:
          -     literal
          -     this
          -     ( expression )
          -     id-expression
          -     lambda-expression
          -     fold-expression
          -     requires-expression
          -+    [: constant-expression :]
          -+    template[: constant-expression :] < template-argument-listopt >
          +
            primary-expression:
          +     literal
          +     this
          +     ( expression )
          +     id-expression
          +     lambda-expression
          +     fold-expression
          +     requires-expression
          ++    [: constant-expression :]
          ++    template[: constant-expression :] < template-argument-listopt >

          5.1.8 [expr.prim.splice] Expression splicing

          @@ -2063,15 +2146,15 @@

          1 Expressions with unary operators group right-to-left.

          -
            unary-expression:
          -     ...
          -     delete-expression
          -+    ^ ::
          -+    ^ namespace-name
          -+    ^ nested-name-specifieropt template-name
          -+    ^ nested-name-specifieropt concept-name
          -+    ^ type-id
          -+    ^ cast-expression
          +
            unary-expression:
          +     ...
          +     delete-expression
          ++    ^ ::
          ++    ^ namespace-name
          ++    ^ nested-name-specifieropt template-name
          ++    ^ nested-name-specifieropt concept-name
          ++    ^ type-id
          ++    ^ cast-expression

          5.1.10 [expr.reflect] The reflection operator

          @@ -2082,17 +2165,17 @@

          1 The unary ^ operator (called the reflection operator) produces a prvalue — called reflection — whose type is the reflection type (i.e., std::meta::info). That reflection represents its operand.

          2 An ambiguity can arise between the interpretation of the operand of the reflection operator as a type-id or a cast-expression; in such cases, the type-id treatment is chosen. Parentheses can be introduced to force the cast-expression interpretation.

          3 [Example

          -
          static_assert(is_type(^int()));    // ^ applies to the type-id "int()"; not the cast "int()"
          -static_assert(!is_type(^(int()))); // ^ applies to the the cast-expression "(int())"
          -
          -template<bool> struct X;
          -consteval void g(std::meta::info r) {
          -  if (r == ^int && true);    // error: ^ applies to the type-id "int&&"
          -  if (r == (^int) && true);  // OK
          -  if (r == ^X < true);       // error: "<" is an angle bracket
          -  if (r == (^X) < true);     // OK
          -}
          -
          +
          static_assert(is_type(^int()));    // ^ applies to the type-id "int()"; not the cast "int()"
          +static_assert(!is_type(^(int()))); // ^ applies to the the cast-expression "(int())"
          +
          +template<bool> struct X;
          +consteval void g(std::meta::info r) {
          +  if (r == ^int && true);    // error: ^ applies to the type-id "int&&"
          +  if (r == (^int) && true);  // OK
          +  if (r == ^X < true);       // error: "<" is an angle bracket
          +  if (r == (^X) < true);     // OK
          +}
          +

          -end example]

          4 When applied to ::, the reflection operator produces a reflection for the global namespace. When applied to a namespace-name, the reflection produces a reflection for the indicated namespace or namespace alias.

          5 When applied to a template-name, the reflection produces a reflection for the indicated template.

          @@ -2100,7 +2183,7 @@

          7 When applied to a type-id, the reflection produces a reflection for the indicated type or type alias.

          8 When applied to a cast-expression, the cast-expression shall be a constant expression (7.7 [expr.const]) or an id-expression (7.5.4 [expr.prim.id]) designating a variable, a function, an enumerator constant, or a nonstatic member. The cast-expression is not evaluated. If the operand of the reflection operator is an id-expression, the result is a reflection for the indicated entity. If the operand is a constant expression, the result is a reflection for the resulting value. If the operand is both an id-expression and a constant expression, the result is a reflection for both the indicated entity and the expression’s (constant) value.

          [ Example:

          -
          constexpr auto r = ^std::vector;
          +
          constexpr auto r = ^std::vector;

          end example ]

          @@ -2159,22 +2242,22 @@

          Add a production to the grammar for attribute-specifier as follows:

          -
            attribute-specifier:
          -     [ [ attribute-using-prefixopt attribute-list ] ]
          -+    [ [ using attribute-namespace :] ]
          -     alignment-specifier
          +
            attribute-specifier:
          +     [ [ attribute-using-prefixopt attribute-list ] ]
          ++    [ [ using attribute-namespace :] ]
          +     alignment-specifier

          and update the grammar for balanced token as follows:

          -
            balanced-token :
          -      ( balanced-token-seqopt )
          -      [ balanced-token-seqopt ]
          -      { balanced-token-seqopt }
          --     any token other than a parenthesis, a bracket, or a brace
          -+     [: balanced-token-seqopt :]
          -+     any token other than (, ), [, ], {, }, [:, or :]
          +
            balanced-token :
          +      ( balanced-token-seqopt )
          +      [ balanced-token-seqopt ]
          +      { balanced-token-seqopt }
          +-     any token other than a parenthesis, a bracket, or a brace
          ++     [: balanced-token-seqopt :]
          ++     any token other than (, ), [, ], {, }, [:, or :]

          Change a sentence in paragraph 4 of 9.12.1 [dcl.attr.grammar] as follows:

          @@ -2186,347 +2269,347 @@

          std::meta::info to 12.5 [over.built]:

          16 For every T, where T is a pointer-to-member type, std::meta::info, or std​::​nullptr_t, there exist candidate operator functions of the form

          -
          bool operator==(T, T);
          -bool operator!=(T, T);
          +
          bool operator==(T, T);
          +bool operator!=(T, T);

          5.2.2 Header <meta> synopsis

          Add a new subsection in 21 [meta] after 21.3 [type.traits]:

          Header <meta> synopsis

          -
          namespace std::meta {
          -  using info = decltype(^::);
          -
          -  // [meta.reflection.names], reflection names and locations
          -  consteval string_view name_of(info r);
          -  consteval string_view qualified_name_of(info r);
          -  consteval string_view display_name_of(info r);
          -  consteval source_location source_location_of(info r);
          -
          -  // [meta.reflection.queries], reflection queries
          -  consteval bool is_public(info r);
          -  consteval bool is_protected(info r);
          -  consteval bool is_private(info r);
          -  consteval bool is_accessible(info r);
          -  consteval bool is_virtual(info r);
          -  consteval bool is_pure_virtual(info r);
          -  consteval bool is_override(info r);
          -  consteval bool is_deleted(info r);
          -  consteval bool is_defaulted(info r);
          -  consteval bool is_explicit(info r);
          -  consteval bool is_bit_field(info r);
          -  consteval bool has_static_storage_duration(info r);
          -  consteval bool has_internal_linkage(info r);
          -  consteval bool has_external_linkage(info r);
          -  consteval bool has_linkage(info r);
          -
          -  consteval bool is_namespace(info r);
          -  consteval bool is_function(info r);
          -  consteval bool is_variable(info r);
          -  consteval bool is_type(info r);
          -  consteval bool is_alias(info r);
          -  consteval bool is_incomplete_type(info r);
          -  consteval bool is_template(info r);
          -  consteval bool is_function_template(info r);
          -  consteval bool is_variable_template(info r);
          -  consteval bool is_class_template(info r);
          -  consteval bool is_alias_template(info r);
          -  consteval bool is_concept(info r);
          -  consteval bool has_template_arguments(info r);
          -  consteval auto is_class_member(info entity) -> bool;
          -  consteval auto is_namespace_member(info entity) -> bool;
          -  consteval bool is_nonstatic_data_member(info r);
          -  consteval bool is_static_member(info r);
          -  consteval bool is_base(info r);
          -  consteval bool is_constructor(info r);
          -  consteval bool is_destructor(info r);
          -  consteval bool is_special_member(info r);
          -
          -  consteval info type_of(info r);
          -  consteval info parent_of(info r);
          -  consteval info dealias(info r);
          -  consteval info template_of(info r);
          -  consteval vector<info> template_arguments_of(info r);
          -
          -  // [meta.reflection.member.queries], reflection member queries
          -  template<class... Fs>
          -    consteval vector<info> members_of(info type, Fs... filters);
          -  template<class... Fs>
          -    consteval vector<info> bases_of(info type, Fs... filters);
          -  consteval vector<info> static_data_members_of(info type);
          -  consteval vector<info> nonstatic_data_members_of(info type);
          -  consteval vector<info> subobjects_of(info type);
          -  consteval vector<info> enumerators_of(info enum_type);
          -
          -  // [meta.reflection.unary.cat], primary type categories
          -  consteval bool is_void(info type);
          -  consteval bool is_null_pointer(info type);
          -  consteval bool is_integral(info type);
          -  consteval bool is_floating_point(info type);
          -  consteval bool is_array(info type);
          -  consteval bool is_pointer(info type);
          -  consteval bool is_lvalue_reference(info type);
          -  consteval bool is_rvalue_reference(info type);
          -  consteval bool is_member_object_pointer(info type);
          -  consteval bool is_member_function_pointer(info type);
          -  consteval bool is_enum(info type);
          -  consteval bool is_union(info type);
          -  consteval bool is_class(info type);
          -  consteval bool is_function(info type);
          -
          -  // [meta.reflection.unary.comp], composite type categories
          -  consteval bool is_reference(info type);
          -  consteval bool is_arithmetic(info type);
          -  consteval bool is_fundamental(info type);
          -  consteval bool is_object(info type);
          -  consteval bool is_scalar(info type);
          -  consteval bool is_compound(info type);
          -  consteval bool is_member_pointer(info type);
          -
          -  // [meta.reflection unary.prop], type properties
          -  consteval bool is_const(info type);
          -  consteval bool is_volatile(info type);
          -  consteval bool is_trivial(info type);
          -  consteval bool is_trivially_copyable(info type);
          -  consteval bool is_standard_layout(info type);
          -  consteval bool is_empty(info type);
          -  consteval bool is_polymorphic(info type);
          -  consteval bool is_abstract(info type);
          -  consteval bool is_final(info type);
          -  consteval bool is_aggregate(info type);
          -  consteval bool is_signed(info type);
          -  consteval bool is_unsigned(info type);
          -  consteval bool is_bounded_array(info type);
          -  consteval bool is_unbounded_array(info type);
          -  consteval bool is_scoped_enum(info type);
          -
          -  consteval bool is_constructible(info type, span<info const> type_args);
          -  consteval bool is_default_constructible(info type);
          -  consteval bool is_copy_constructible(info type);
          -  consteval bool is_move_constructible(info type);
          -
          -  consteval bool is_assignable(info dst_type, info src_type);
          -  consteval bool is_copy_assignable(info type);
          -  consteval bool is_move_assignable(info type);
          -
          -  consteval bool is_swappable_with(info dst_type, info src_type);
          -  consteval bool is_swappable(info type);
          -
          -  consteval bool is_destructible(info type);
          -
          -  consteval bool is_trivially_constructible(info type, span<info const> type_args);
          -  consteval bool is_trivially_default_constructible(info type);
          -  consteval bool is_trivially_copy_constructible(info type);
          -  consteval bool is_trivially_move_constructible(info type);
          -
          -  consteval bool is_trivially_assignable(info dst_type, info src_type);
          -  consteval bool is_trivially_copy_assignable(info type);
          -  consteval bool is_trivially_move_assignable(info type);
          -  consteval bool is_trivially_destructible(info type);
          -
          -  consteval bool is_nothrow_constructible(info type, span<info const> type_args);
          -  consteval bool is_nothrow_default_constructible(info type);
          -  consteval bool is_nothrow_copy_constructible(info type);
          -  consteval bool is_nothrow_move_constructible(info type);
          -
          -  consteval bool is_nothrow_assignable(info dst_type, info src_type);
          -  consteval bool is_nothrow_copy_assignable(info type);
          -  consteval bool is_nothrow_move_assignable(info type);
          -
          -  consteval bool is_nothrow_swappable_with(info dst_type, info src_type);
          -  consteval bool is_nothrow_swappable(info type);
          -
          -  consteval bool is_nothrow_destructible(info type);
          -
          -  consteval bool is_implicit_lifetime(info type);
          -
          -  consteval bool has_virtual_destructor(info type);
          -
          -  consteval bool has_unique_object_representations(info type);
          -
          -  consteval bool reference_constructs_from_temporary(info dst_type, info src_type);
          -  consteval bool reference_converts_from_temporary(info dst_type, info src_type);
          -
          -  // [meta.reflection.unary.prop.query], type property queries
          -  consteval size_t alignment_of(info type);
          -  consteval size_t rank(info type);
          -  consteval size_t extent(info type, unsigned i = 0);
          -
          -  // [meta.reflection.rel], type relations
          -  consteval bool is_same(info type1, info type2);
          -  consteval bool is_base_of(info base_type, info derived_type);
          -  consteval bool is_convertible(info src_type, info dst_type);
          -  consteval bool is_nothrow_convertible(info src_type, info dst_type);
          -  consteval bool is_layout_compatible(info type1, info type2);
          -  consteval bool is_pointer_interconvertible_base_of(info base_type, info derived_type);
          -
          -  consteval bool is_invocable(info type, span<const info> type_args);
          -  consteval bool is_invocable_r(info result_type, info type, span<const info> type_args);
          -
          -  consteval bool is_nothrow_invocable(info type, span<const info> type_args);
          -  consteval bool is_nothrow_invocable_r(info result_type, info type, span<const info> type_args);
          -
          -  // [meta.reflection.trans.cv], const-volatile modifications
          -  consteval info remove_const(info type);
          -  consteval info remove_volatile(info type);
          -  consteval info remove_cv(info type);
          -  consteval info add_const(info type);
          -  consteval info add_volatile(info type);
          -  consteval info add_cv(info type);
          -
          -  // [meta.reflection.trans.ref], reference modifications
          -  consteval info remove_reference(info type);
          -  consteval info add_lvalue_reference(info type);
          -  consteval info add_rvalue_reference(info type);
          -
          -  // [meta.reflection.trans.sign], sign modifications
          -  consteval info make_signed(info type);
          -  consteval info make_unsigned(info type);
          -
          -  // [meta.reflection.trans.arr], array modifications
          -  consteval info remove_extent(info type);
          -  consteval info remove_all_extents(info type);
          -
          -  // [meta.reflection.trans.ptr], pointer modifications
          -  consteval info remove_pointer(info type);
          -  consteval info add_pointer(info type);
          -
          -  // [meta.reflection.trans.other], other transformations
          -  consteval info remove_cvref(info type);
          -  consteval info decay(info type);
          -  consteval info common_type(span<const info> type_args);
          -  consteval info common_reference(span<const info> type_args);
          -  consteval info underlying_type(info type);
          -  consteval info invoke_result(info type, span<const info> type_args);
          -  consteval info unwrap_reference(info type);
          -  consteval info unwrap_ref_decay(info type);
          -}
          +
          namespace std::meta {
          +  using info = decltype(^::);
          +
          +  // [meta.reflection.names], reflection names and locations
          +  consteval string_view name_of(info r);
          +  consteval string_view qualified_name_of(info r);
          +  consteval string_view display_name_of(info r);
          +  consteval source_location source_location_of(info r);
          +
          +  // [meta.reflection.queries], reflection queries
          +  consteval bool is_public(info r);
          +  consteval bool is_protected(info r);
          +  consteval bool is_private(info r);
          +  consteval bool is_accessible(info r);
          +  consteval bool is_virtual(info r);
          +  consteval bool is_pure_virtual(info r);
          +  consteval bool is_override(info r);
          +  consteval bool is_deleted(info r);
          +  consteval bool is_defaulted(info r);
          +  consteval bool is_explicit(info r);
          +  consteval bool is_bit_field(info r);
          +  consteval bool has_static_storage_duration(info r);
          +  consteval bool has_internal_linkage(info r);
          +  consteval bool has_external_linkage(info r);
          +  consteval bool has_linkage(info r);
          +
          +  consteval bool is_namespace(info r);
          +  consteval bool is_function(info r);
          +  consteval bool is_variable(info r);
          +  consteval bool is_type(info r);
          +  consteval bool is_alias(info r);
          +  consteval bool is_incomplete_type(info r);
          +  consteval bool is_template(info r);
          +  consteval bool is_function_template(info r);
          +  consteval bool is_variable_template(info r);
          +  consteval bool is_class_template(info r);
          +  consteval bool is_alias_template(info r);
          +  consteval bool is_concept(info r);
          +  consteval bool has_template_arguments(info r);
          +  consteval auto is_class_member(info entity) -> bool;
          +  consteval auto is_namespace_member(info entity) -> bool;
          +  consteval bool is_nonstatic_data_member(info r);
          +  consteval bool is_static_member(info r);
          +  consteval bool is_base(info r);
          +  consteval bool is_constructor(info r);
          +  consteval bool is_destructor(info r);
          +  consteval bool is_special_member(info r);
          +
          +  consteval info type_of(info r);
          +  consteval info parent_of(info r);
          +  consteval info dealias(info r);
          +  consteval info template_of(info r);
          +  consteval vector<info> template_arguments_of(info r);
          +
          +  // [meta.reflection.member.queries], reflection member queries
          +  template<class... Fs>
          +    consteval vector<info> members_of(info type, Fs... filters);
          +  template<class... Fs>
          +    consteval vector<info> bases_of(info type, Fs... filters);
          +  consteval vector<info> static_data_members_of(info type);
          +  consteval vector<info> nonstatic_data_members_of(info type);
          +  consteval vector<info> subobjects_of(info type);
          +  consteval vector<info> enumerators_of(info enum_type);
          +
          +  // [meta.reflection.unary.cat], primary type categories
          +  consteval bool is_void(info type);
          +  consteval bool is_null_pointer(info type);
          +  consteval bool is_integral(info type);
          +  consteval bool is_floating_point(info type);
          +  consteval bool is_array(info type);
          +  consteval bool is_pointer(info type);
          +  consteval bool is_lvalue_reference(info type);
          +  consteval bool is_rvalue_reference(info type);
          +  consteval bool is_member_object_pointer(info type);
          +  consteval bool is_member_function_pointer(info type);
          +  consteval bool is_enum(info type);
          +  consteval bool is_union(info type);
          +  consteval bool is_class(info type);
          +  consteval bool is_function(info type);
          +
          +  // [meta.reflection.unary.comp], composite type categories
          +  consteval bool is_reference(info type);
          +  consteval bool is_arithmetic(info type);
          +  consteval bool is_fundamental(info type);
          +  consteval bool is_object(info type);
          +  consteval bool is_scalar(info type);
          +  consteval bool is_compound(info type);
          +  consteval bool is_member_pointer(info type);
          +
          +  // [meta.reflection unary.prop], type properties
          +  consteval bool is_const(info type);
          +  consteval bool is_volatile(info type);
          +  consteval bool is_trivial(info type);
          +  consteval bool is_trivially_copyable(info type);
          +  consteval bool is_standard_layout(info type);
          +  consteval bool is_empty(info type);
          +  consteval bool is_polymorphic(info type);
          +  consteval bool is_abstract(info type);
          +  consteval bool is_final(info type);
          +  consteval bool is_aggregate(info type);
          +  consteval bool is_signed(info type);
          +  consteval bool is_unsigned(info type);
          +  consteval bool is_bounded_array(info type);
          +  consteval bool is_unbounded_array(info type);
          +  consteval bool is_scoped_enum(info type);
          +
          +  consteval bool is_constructible(info type, span<info const> type_args);
          +  consteval bool is_default_constructible(info type);
          +  consteval bool is_copy_constructible(info type);
          +  consteval bool is_move_constructible(info type);
          +
          +  consteval bool is_assignable(info dst_type, info src_type);
          +  consteval bool is_copy_assignable(info type);
          +  consteval bool is_move_assignable(info type);
          +
          +  consteval bool is_swappable_with(info dst_type, info src_type);
          +  consteval bool is_swappable(info type);
          +
          +  consteval bool is_destructible(info type);
          +
          +  consteval bool is_trivially_constructible(info type, span<info const> type_args);
          +  consteval bool is_trivially_default_constructible(info type);
          +  consteval bool is_trivially_copy_constructible(info type);
          +  consteval bool is_trivially_move_constructible(info type);
          +
          +  consteval bool is_trivially_assignable(info dst_type, info src_type);
          +  consteval bool is_trivially_copy_assignable(info type);
          +  consteval bool is_trivially_move_assignable(info type);
          +  consteval bool is_trivially_destructible(info type);
          +
          +  consteval bool is_nothrow_constructible(info type, span<info const> type_args);
          +  consteval bool is_nothrow_default_constructible(info type);
          +  consteval bool is_nothrow_copy_constructible(info type);
          +  consteval bool is_nothrow_move_constructible(info type);
          +
          +  consteval bool is_nothrow_assignable(info dst_type, info src_type);
          +  consteval bool is_nothrow_copy_assignable(info type);
          +  consteval bool is_nothrow_move_assignable(info type);
          +
          +  consteval bool is_nothrow_swappable_with(info dst_type, info src_type);
          +  consteval bool is_nothrow_swappable(info type);
          +
          +  consteval bool is_nothrow_destructible(info type);
          +
          +  consteval bool is_implicit_lifetime(info type);
          +
          +  consteval bool has_virtual_destructor(info type);
          +
          +  consteval bool has_unique_object_representations(info type);
          +
          +  consteval bool reference_constructs_from_temporary(info dst_type, info src_type);
          +  consteval bool reference_converts_from_temporary(info dst_type, info src_type);
          +
          +  // [meta.reflection.unary.prop.query], type property queries
          +  consteval size_t alignment_of(info type);
          +  consteval size_t rank(info type);
          +  consteval size_t extent(info type, unsigned i = 0);
          +
          +  // [meta.reflection.rel], type relations
          +  consteval bool is_same(info type1, info type2);
          +  consteval bool is_base_of(info base_type, info derived_type);
          +  consteval bool is_convertible(info src_type, info dst_type);
          +  consteval bool is_nothrow_convertible(info src_type, info dst_type);
          +  consteval bool is_layout_compatible(info type1, info type2);
          +  consteval bool is_pointer_interconvertible_base_of(info base_type, info derived_type);
          +
          +  consteval bool is_invocable(info type, span<const info> type_args);
          +  consteval bool is_invocable_r(info result_type, info type, span<const info> type_args);
          +
          +  consteval bool is_nothrow_invocable(info type, span<const info> type_args);
          +  consteval bool is_nothrow_invocable_r(info result_type, info type, span<const info> type_args);
          +
          +  // [meta.reflection.trans.cv], const-volatile modifications
          +  consteval info remove_const(info type);
          +  consteval info remove_volatile(info type);
          +  consteval info remove_cv(info type);
          +  consteval info add_const(info type);
          +  consteval info add_volatile(info type);
          +  consteval info add_cv(info type);
          +
          +  // [meta.reflection.trans.ref], reference modifications
          +  consteval info remove_reference(info type);
          +  consteval info add_lvalue_reference(info type);
          +  consteval info add_rvalue_reference(info type);
          +
          +  // [meta.reflection.trans.sign], sign modifications
          +  consteval info make_signed(info type);
          +  consteval info make_unsigned(info type);
          +
          +  // [meta.reflection.trans.arr], array modifications
          +  consteval info remove_extent(info type);
          +  consteval info remove_all_extents(info type);
          +
          +  // [meta.reflection.trans.ptr], pointer modifications
          +  consteval info remove_pointer(info type);
          +  consteval info add_pointer(info type);
          +
          +  // [meta.reflection.trans.other], other transformations
          +  consteval info remove_cvref(info type);
          +  consteval info decay(info type);
          +  consteval info common_type(span<const info> type_args);
          +  consteval info common_reference(span<const info> type_args);
          +  consteval info underlying_type(info type);
          +  consteval info invoke_result(info type, span<const info> type_args);
          +  consteval info unwrap_reference(info type);
          +  consteval info unwrap_ref_decay(info type);
          +}

          5.2.3 [meta.reflection.names] Reflection names and locations

          -
          consteval string_view name_of(info r);
          -consteval string_view qualified_name_of(info r);
          +
          consteval string_view name_of(info r);
          +consteval string_view qualified_name_of(info r);

          1 Returns: If r designates a declared entity X, then the unqualified and qualified names of X, respectively. Otherwise, an empty string_view.

          -
          consteval string_view display_name_of(info r);
          +
          consteval string_view display_name_of(info r);

          2 Returns: An implementation-defined string suitable for identifying the reflected construct.

          -
          consteval source_location source_location_of(info r);
          +
          consteval source_location source_location_of(info r);

          3 Returns: An implementation-defined source_location corresponding to the reflected construct.

          5.2.4 [meta.reflection.queries] Reflection queries

          -
          consteval bool is_public(info r);
          -consteval bool is_protected(info r);
          -consteval bool is_private(info r);
          +
          consteval bool is_public(info r);
          +consteval bool is_protected(info r);
          +consteval bool is_private(info r);

          1 Returns: true if r designates a class member or base class that is public, protected, or private, respectively. Otherwise, false.

          -
          consteval bool is_accessible(info r);
          +
          consteval bool is_accessible(info r);

          2 Returns: TODO

          -
          consteval bool is_virtual(info r);
          +
          consteval bool is_virtual(info r);

          3 Returns: true if r designates a either a virtual member function or a virtual base class. Otherwise, false.

          -
          consteval bool is_pure_virtual(info r);
          -consteval bool is_override(info r);
          +
          consteval bool is_pure_virtual(info r);
          +consteval bool is_override(info r);

          4 Returns: true if r designates a member function that is pure virtual or overrides another member function, respectively. Otherwise, false.

          -
          consteval bool is_deleted(info r);
          +
          consteval bool is_deleted(info r);

          5 Returns: true if r designates a function or member function that is defined as deleted. Otherwise, false.

          -
          consteval bool is_defaulted(info r);
          +
          consteval bool is_defaulted(info r);

          6 Returns: true if r designates a member function that is defined as defaulted. Otherwise, false.

          -
          consteval bool is_explicit(info r);
          +
          consteval bool is_explicit(info r);

          7 Returns: true if r designates a member function that is declared explicit. Otherwise, false.

          -
          consteval bool is_bit_field(info r);
          +
          consteval bool is_bit_field(info r);

          8 Returns: true if r designates a bit-field. Otherwise, false.

          -
          consteval bool has_static_storage_duration(info r);
          +
          consteval bool has_static_storage_duration(info r);

          9 Returns: true if r designates an object that has static storage duration. Otherwise, false.

          -
          consteval bool has_internal_linkage(info r);
          -consteval bool has_external_linkage(info r);
          -consteval bool has_linkage(info r);
          +
          consteval bool has_internal_linkage(info r);
          +consteval bool has_external_linkage(info r);
          +consteval bool has_linkage(info r);

          10 Returns: true if r designates an entity that has internal linkage, external linkage, or any linkage, respectively ([basic.link]). Otherwise, false.

          -
          consteval bool is_namespace(info r);
          +
          consteval bool is_namespace(info r);

          11 Returns: true if r designates a namespace or namespace alias. Otherwise, false.

          -
          consteval bool is_function(info r);
          +
          consteval bool is_function(info r);

          12 Returns: true if r designates a function or member function. Otherwise, false.

          -
          consteval bool is_variable(info r);
          +
          consteval bool is_variable(info r);

          13 Returns: true if r designates a variable. Otherwise, false.

          -
          consteval bool is_type(info r);
          +
          consteval bool is_type(info r);

          14 Returns: true if r designates a type or a type alias. Otherwise, false.

          -
          consteval bool is_alias(info r);
          +
          consteval bool is_alias(info r);

          15 Returns: true if r designates a type alias, alias template, or namespace alias. Otherwise, false.

          -
          consteval bool is_incomplete_type(info r);
          +
          consteval bool is_incomplete_type(info r);

          16 Returns: true if delias(r) designates an incomplete type. Otherwise, false.

          -
          consteval bool is_template(info r);
          +
          consteval bool is_template(info r);

          17 Returns: true if r designates a function template, class template, variable template, or alias template. Otherwise, false.

          18 Note 1: A template specialization is not a template. is_template(^std::vector) is true but is_template(^std::vector<int>) is false. — end note ]

          -
          consteval bool is_function_template(info r);
          -consteval bool is_variable_template(info r);
          -consteval bool is_class_template(info r);
          -consteval bool is_alias_template(info r);
          -consteval bool is_concept(info r);
          +
          consteval bool is_function_template(info r);
          +consteval bool is_variable_template(info r);
          +consteval bool is_class_template(info r);
          +consteval bool is_alias_template(info r);
          +consteval bool is_concept(info r);

          19 Returns: true if r designates a function template, class template, variable template, alias template, or concept, respectively. Otherwise, false.

          -
          consteval bool has_template_arguments(info r);
          +
          consteval bool has_template_arguments(info r);

          20 Returns: true if r designates an instantiation of a function template, variable template, class template, or an alias template. Otherwise, false.

          -
          consteval auto is_class_member(info entity) -> bool;
          -consteval auto is_namespace_member(info entity) -> bool;
          -consteval bool is_nonstatic_data_member(info r);
          -consteval bool is_static_member(info r);
          -consteval bool is_base(info r);
          -consteval bool is_constructor(info r);
          -consteval bool is_destructor(info r);
          -consteval bool is_special_member(info r);
          +
          consteval auto is_class_member(info entity) -> bool;
          +consteval auto is_namespace_member(info entity) -> bool;
          +consteval bool is_nonstatic_data_member(info r);
          +consteval bool is_static_member(info r);
          +consteval bool is_base(info r);
          +consteval bool is_constructor(info r);
          +consteval bool is_destructor(info r);
          +consteval bool is_special_member(info r);

          21 Returns: true if r designates a class member, namespace member, non-static data member, static member, base class member, constructor, destructor, or special member, respectively. Otherwise, false.

          -
          consteval info type_of(info r);
          +
          consteval info type_of(info r);

          22 Mandates: r designates a typed entity.

          23 Returns: A reflection of the type of that entity.

          -
          consteval info parent_of(info r);
          +
          consteval info parent_of(info r);

          24 Mandates: r designates a member of a class or a namespace.

          25 Returns: A reflection of the that entity’s immediately enclosing class or namespace.

          -
          consteval info dealias(info r);
          +
          consteval info dealias(info r);

          26 Returns: If r designates a type alias or a namespace alias, a reflection designating the underlying entity. Otherwise, r.

          27 [Example

          -
          using X = int;
          -using Y = X;
          -static_assert(dealias(^int) == ^int);
          -static_assert(dealias(^X) == ^int);
          -static_assert(dealias(^Y) == ^int);
          +
          using X = int;
          +using Y = X;
          +static_assert(dealias(^int) == ^int);
          +static_assert(dealias(^X) == ^int);
          +static_assert(dealias(^Y) == ^int);

          -end example]

          -
          consteval info template_of(info r);
          -consteval vector<info> template_arguments_of(info r);
          +
          consteval info template_of(info r);
          +consteval vector<info> template_arguments_of(info r);

          28 Mandates: has_template_arguments(r) is true.

          29 Returns: A reflection of the template of r, and the reflections of the template arguments of, the specialization designated by r, respectively.

          30 [Example:

          -
          template <class T, class U=T> struct Pair { };
          -template <class T> using PairPtr = Pair<T*>;
          -
          -static_assert(template_of(^Pair<int>) == ^Pair);
          -static_assert(template_arguments_of(^Pair<int>).size() == 2);
          -
          -static_assert(template_of(^PairPtr<int>) == ^PairPtr);
          -static_assert(template_arguments_of(^PairPtr<int>).size() == 1);
          +
          template <class T, class U=T> struct Pair { };
          +template <class T> using PairPtr = Pair<T*>;
          +
          +static_assert(template_of(^Pair<int>) == ^Pair);
          +static_assert(template_arguments_of(^Pair<int>).size() == 2);
          +
          +static_assert(template_of(^PairPtr<int>) == ^PairPtr);
          +static_assert(template_arguments_of(^PairPtr<int>).size() == 1);

          -end example]

          5.2.5 [meta.reflection.member.queries], Reflection member queries

          -
          template<class... Fs>
          -  consteval vector<info> members_of(info r, Fs... filters);
          +
          template<class... Fs>
          +  consteval vector<info> members_of(info r, Fs... filters);

          1 Mandates: r is a reflection designating either a class type or a namespace and (std::predicate<Fs, info> && ...) is true.

          2 Returns: A vector containing the reflections of all the direct members m of the entity designated by r such that (filters(m) && ...) is true. Data members are returned in the order in which they are declared, but the order of member functions and member types is unspecified. Note 1: Base classes are not members. — end note ]

          -
          template<class... Fs>
          -  consteval vector<info> bases_of(info type, Fs... filters);
          +
          template<class... Fs>
          +  consteval vector<info> bases_of(info type, Fs... filters);

          3 Mandates: type designates a type and (std::predicate<Fs, info> && ...) is true.

          4 Returns: Let C be the type designated by type. A vector containing the reflections of all the direct base classes, if any, of C such that (filters(class_type) && ...) is true. The base classes are returned in the order in which they appear the base-specifier-list of C.

          -
          consteval vector<info> static_data_members_of(info type);
          +
          consteval vector<info> static_data_members_of(info type);

          5 Mandates: type designates a type.

          6 Effects: Equivalent to: return members_of(type, is_variable);

          -
          consteval vector<info> nonstatic_data_members_of(info type);
          +
          consteval vector<info> nonstatic_data_members_of(info type);

          7 Mandates: type designates a type.

          8 Effects: Equivalent to: return members_of(type, is_nonstatic_data_member);

          -
          consteval vector<info> subobjects_of(info type);
          +
          consteval vector<info> subobjects_of(info type);

          9 Mandates: type designates a type.

          10 Returns: A vector containing all the reflections in bases_of(type) followed by all the reflections in nonstatic_data_members_of(type).

          -
          consteval vector<info> enumerators_of(info enum_type);
          +
          consteval vector<info> enumerators_of(info enum_type);

          11 Mandates: enum_type designates an enumeration.

          12 Returns: A vector containing the reflections of each enumerator of the enumeration designated by enum_type, in the order in which they are declared.

          @@ -2542,27 +2625,27 @@

          1 For any type T, for each function std::meta::TRAIT defined in this clause, std::meta::TRAIT(^T) equals the value of the corresponding unary type trait std::TRAIT_v<T> as specified in 21.3.5.2 [meta.unary.cat].

          -
          consteval bool is_void(info type);
          -consteval bool is_null_pointer(info type);
          -consteval bool is_integral(info type);
          -consteval bool is_floating_point(info type);
          -consteval bool is_array(info type);
          -consteval bool is_pointer(info type);
          -consteval bool is_lvalue_reference(info type);
          -consteval bool is_rvalue_reference(info type);
          -consteval bool is_member_object_pointer(info type);
          -consteval bool is_member_function_pointer(info type);
          -consteval bool is_enum(info type);
          -consteval bool is_union(info type);
          -consteval bool is_class(info type);
          -consteval bool is_function(info type);
          +
          consteval bool is_void(info type);
          +consteval bool is_null_pointer(info type);
          +consteval bool is_integral(info type);
          +consteval bool is_floating_point(info type);
          +consteval bool is_array(info type);
          +consteval bool is_pointer(info type);
          +consteval bool is_lvalue_reference(info type);
          +consteval bool is_rvalue_reference(info type);
          +consteval bool is_member_object_pointer(info type);
          +consteval bool is_member_function_pointer(info type);
          +consteval bool is_enum(info type);
          +consteval bool is_union(info type);
          +consteval bool is_class(info type);
          +consteval bool is_function(info type);

          2 [Example

          -
          // an example implementation
          -namespace std::meta {
          -  consteval bool is_void(info type) {
          -    return value_of<bool>(substitute(^is_void_v, {type}));
          -  }
          -}
          +
          // an example implementation
          +namespace std::meta {
          +  consteval bool is_void(info type) {
          +    return value_of<bool>(substitute(^is_void_v, {type}));
          +  }
          +}

          -end example]

          @@ -2570,13 +2653,13 @@

          1 For any type T, for each function std::meta::TRAIT defined in this clause, std::meta::TRAIT(^T) equals the value of the corresponding unary type trait std::TRAIT_v<T> as specified in 21.3.5.3 [meta.unary.comp].

          -
          consteval bool is_reference(info type);
          -consteval bool is_arithmetic(info type);
          -consteval bool is_fundamental(info type);
          -consteval bool is_object(info type);
          -consteval bool is_scalar(info type);
          -consteval bool is_compound(info type);
          -consteval bool is_member_pointer(info type);
          +
          consteval bool is_reference(info type);
          +consteval bool is_arithmetic(info type);
          +consteval bool is_fundamental(info type);
          +consteval bool is_object(info type);
          +consteval bool is_scalar(info type);
          +consteval bool is_compound(info type);
          +consteval bool is_member_pointer(info type);

          5.2.6.3 [meta.reflection.unary.prop] Type properties

          @@ -2585,68 +2668,68 @@

          1 For any type T, for each function std::meta::UNARY-TRAIT defined in this clause with signature bool(std::meta::info), std::meta::UNARY-TRAIT(^T) equals the value of the corresponding type property std::UNARY-TRAIT_v<T> as specified in 21.3.5.4 [meta.unary.prop].

          2 For any types T and U, for each function std::meta::BINARY-TRAIT defined in this clause with signature bool(std::meta::info, std::meta::info), std::meta::BINARY-TRAIT(^T, ^U) equals the value of the corresponding type property std::BINARY-TRAIT_v<T, U> as specified in 21.3.5.4 [meta.unary.prop].

          3 For any type T and pack of types U..., for each function std::meta::VARIADIC-TRAIT defined in this clause with signature bool(std::meta::info, std::span<const std::meta::info>), std::meta::VARIADIC-TRAIT(^T, {^U...}) equals the value of the corresponding type property std::VARIADIC-TRAIT_v<T, U...> as specified in 21.3.5.4 [meta.unary.prop].

          -
          consteval bool is_const(info type);
          -consteval bool is_volatile(info type);
          -consteval bool is_trivial(info type);
          -consteval bool is_trivially_copyable(info type);
          -consteval bool is_standard_layout(info type);
          -consteval bool is_empty(info type);
          -consteval bool is_polymorphic(info type);
          -consteval bool is_abstract(info type);
          -consteval bool is_final(info type);
          -consteval bool is_aggregate(info type);
          -consteval bool is_signed(info type);
          -consteval bool is_unsigned(info type);
          -consteval bool is_bounded_array(info type);
          -consteval bool is_unbounded_array(info type);
          -consteval bool is_scoped_enum(info type);
          -
          -consteval bool is_constructible(info type, span<info const> type_args);
          -consteval bool is_default_constructible(info type);
          -consteval bool is_copy_constructible(info type);
          -consteval bool is_move_constructible(info type);
          -
          -consteval bool is_assignable(info dst_type, info src_type);
          -consteval bool is_copy_assignable(info type);
          -consteval bool is_move_assignable(info type);
          -
          -consteval bool is_swappable_with(info dst_type, info src_type);
          -consteval bool is_swappable(info type);
          -
          -consteval bool is_destructible(info type);
          -
          -consteval bool is_trivially_constructible(info type, span<info const> type_args);
          -consteval bool is_trivially_default_constructible(info type);
          -consteval bool is_trivially_copy_constructible(info type);
          -consteval bool is_trivially_move_constructible(info type);
          -
          -consteval bool is_trivially_assignable(info dst_type, info src_type);
          -consteval bool is_trivially_copy_assignable(info type);
          -consteval bool is_trivially_move_assignable(info type);
          -consteval bool is_trivially_destructible(info type);
          -
          -consteval bool is_nothrow_constructible(info type, span<info const> type_args);
          -consteval bool is_nothrow_default_constructible(info type);
          -consteval bool is_nothrow_copy_constructible(info type);
          -consteval bool is_nothrow_move_constructible(info type);
          -
          -consteval bool is_nothrow_assignable(info dst_type, info src_type);
          -consteval bool is_nothrow_copy_assignable(info type);
          -consteval bool is_nothrow_move_assignable(info type);
          -
          -consteval bool is_nothrow_swappable_with(info dst_type, info src_type);
          -consteval bool is_nothrow_swappable(info type);
          -
          -consteval bool is_nothrow_destructible(info type);
          -
          -consteval bool is_implicit_lifetime(info type);
          -
          -consteval bool has_virtual_destructor(info type);
          -
          -consteval bool has_unique_object_representations(info type);
          -
          -consteval bool reference_constructs_from_temporary(info dst_type, info src_type);
          -consteval bool reference_converts_from_temporary(info dst_type, info src_type);
          +
          consteval bool is_const(info type);
          +consteval bool is_volatile(info type);
          +consteval bool is_trivial(info type);
          +consteval bool is_trivially_copyable(info type);
          +consteval bool is_standard_layout(info type);
          +consteval bool is_empty(info type);
          +consteval bool is_polymorphic(info type);
          +consteval bool is_abstract(info type);
          +consteval bool is_final(info type);
          +consteval bool is_aggregate(info type);
          +consteval bool is_signed(info type);
          +consteval bool is_unsigned(info type);
          +consteval bool is_bounded_array(info type);
          +consteval bool is_unbounded_array(info type);
          +consteval bool is_scoped_enum(info type);
          +
          +consteval bool is_constructible(info type, span<info const> type_args);
          +consteval bool is_default_constructible(info type);
          +consteval bool is_copy_constructible(info type);
          +consteval bool is_move_constructible(info type);
          +
          +consteval bool is_assignable(info dst_type, info src_type);
          +consteval bool is_copy_assignable(info type);
          +consteval bool is_move_assignable(info type);
          +
          +consteval bool is_swappable_with(info dst_type, info src_type);
          +consteval bool is_swappable(info type);
          +
          +consteval bool is_destructible(info type);
          +
          +consteval bool is_trivially_constructible(info type, span<info const> type_args);
          +consteval bool is_trivially_default_constructible(info type);
          +consteval bool is_trivially_copy_constructible(info type);
          +consteval bool is_trivially_move_constructible(info type);
          +
          +consteval bool is_trivially_assignable(info dst_type, info src_type);
          +consteval bool is_trivially_copy_assignable(info type);
          +consteval bool is_trivially_move_assignable(info type);
          +consteval bool is_trivially_destructible(info type);
          +
          +consteval bool is_nothrow_constructible(info type, span<info const> type_args);
          +consteval bool is_nothrow_default_constructible(info type);
          +consteval bool is_nothrow_copy_constructible(info type);
          +consteval bool is_nothrow_move_constructible(info type);
          +
          +consteval bool is_nothrow_assignable(info dst_type, info src_type);
          +consteval bool is_nothrow_copy_assignable(info type);
          +consteval bool is_nothrow_move_assignable(info type);
          +
          +consteval bool is_nothrow_swappable_with(info dst_type, info src_type);
          +consteval bool is_nothrow_swappable(info type);
          +
          +consteval bool is_nothrow_destructible(info type);
          +
          +consteval bool is_implicit_lifetime(info type);
          +
          +consteval bool has_virtual_destructor(info type);
          +
          +consteval bool has_unique_object_representations(info type);
          +
          +consteval bool reference_constructs_from_temporary(info dst_type, info src_type);
          +consteval bool reference_converts_from_temporary(info dst_type, info src_type);

          5.2.6.4 [meta.reflection.unary.prop.query] Type property queries

          @@ -2654,9 +2737,9 @@

          1 For any type T, for each function std::meta::PROP defined in this clause with signature size_t(std::meta::info), std::meta::PROP(^T) equals the value of the corresponding type property std::PROP_v<T> as specified in 21.3.6 [meta.unary.prop.query].

          2 For any type T and unsigned integer value I, std::meta::extent(^T, I) equals std::extent_v<T, I> ([meta.unary.prop.query]).

          -
          consteval size_t alignment_of(info type);
          -consteval size_t rank(info type);
          -consteval size_t extent(info type, unsigned i = 0);
          +
          consteval size_t alignment_of(info type);
          +consteval size_t rank(info type);
          +consteval size_t extent(info type, unsigned i = 0);

          5.2.7 [meta.reflection.rel], Type relations

          @@ -2666,18 +2749,18 @@

          2 For any types T and U, for each function std::meta::REL defined in this clause with signature bool(std::meta::info, std::meta::info), std::meta::REL(^T, ^U) equals the value of the corresponding type relation std::REL_v<T, U> as specified in 21.3.7 [meta.rel].

          3 For any type T and pack of types U..., for each function std::meta::VARIADIC-REL defined in this clause with signature bool(std::meta::info, std::span<const std::meta::info>), std::meta::VARIADIC-REL(^T, {^U...}) equals the value of the corresponding type relation std::VARIADIC-REL_v<T, U...> as specified in 21.3.7 [meta.rel].

          4 For any types T and R and pack of types U..., for each function std::meta::VARIADIC-REL-R defined in this clause with signature bool(std::meta::info, std::meta::info, std::span<const std::meta::info>), std::meta::VARIADIC-REL-R(^R, ^T, {^U...}) equals the value of the corresponding type relation std::VARIADIC-REL-R_v<R, T, U...> as specified in 21.3.7 [meta.rel].

          -
          consteval bool is_same(info type1, info type2);
          -consteval bool is_base_of(info base_type, info derived_type);
          -consteval bool is_convertible(info src_type, info dst_type);
          -consteval bool is_nothrow_convertible(info src_type, info dst_type);
          -consteval bool is_layout_compatible(info type1, info type2);
          -consteval bool is_pointer_interconvertible_base_of(info base_type, info derived_type);
          -
          -consteval bool is_invocable(info type, span<const info> type_args);
          -consteval bool is_invocable_r(info result_type, info type, span<const info> type_args);
          -
          -consteval bool is_nothrow_invocable(info type, span<const info> type_args);
          -consteval bool is_nothrow_invocable_r(info result_type, info type, span<const info> type_args);
          +
          consteval bool is_same(info type1, info type2);
          +consteval bool is_base_of(info base_type, info derived_type);
          +consteval bool is_convertible(info src_type, info dst_type);
          +consteval bool is_nothrow_convertible(info src_type, info dst_type);
          +consteval bool is_layout_compatible(info type1, info type2);
          +consteval bool is_pointer_interconvertible_base_of(info base_type, info derived_type);
          +
          +consteval bool is_invocable(info type, span<const info> type_args);
          +consteval bool is_invocable_r(info result_type, info type, span<const info> type_args);
          +
          +consteval bool is_nothrow_invocable(info type, span<const info> type_args);
          +consteval bool is_nothrow_invocable_r(info result_type, info type, span<const info> type_args);

          5 Note 1: If t is a reflection of the type int and u is a reflection of an alias to the type int, then t == u is false but is_same(t, u) is true. t == dealias(u) is also true. — end note ].

          @@ -2691,45 +2774,45 @@

          1 For any type T, for each function std::meta::MOD defined in this clause, std::meta::MOD(^T) returns the reflection of the corresponding type std::MOD_t<T> as specified in 21.3.8.2 [meta.trans.cv].

          -
          consteval info remove_const(info type);
          -consteval info remove_volatile(info type);
          -consteval info remove_cv(info type);
          -consteval info add_const(info type);
          -consteval info add_volatile(info type);
          -consteval info add_cv(info type);
          +
          consteval info remove_const(info type);
          +consteval info remove_volatile(info type);
          +consteval info remove_cv(info type);
          +consteval info add_const(info type);
          +consteval info add_volatile(info type);
          +consteval info add_cv(info type);

          5.2.8.2 [meta.reflection.trans.ref], Reference modifications

          1 For any type T, for each function std::meta::MOD defined in this clause, std::meta::MOD(^T) returns the reflection of the corresponding type std::MOD_t<T> as specified in 21.3.8.3 [meta.trans.ref].

          -
          consteval info remove_reference(info type);
          -consteval info add_lvalue_reference(info type);
          -consteval info add_rvalue_reference(info type);
          +
          consteval info remove_reference(info type);
          +consteval info add_lvalue_reference(info type);
          +consteval info add_rvalue_reference(info type);

          5.2.8.3 [meta.reflection.trans.sign], Sign modifications

          1 For any type T, for each function std::meta::MOD defined in this clause, std::meta::MOD(^T) returns the reflection of the corresponding type std::MOD_t<T> as specified in 21.3.8.4 [meta.trans.sign].

          -
          consteval info make_signed(info type);
          -consteval info make_unsigned(info type);
          +
          consteval info make_signed(info type);
          +consteval info make_unsigned(info type);

          5.2.8.4 [meta.reflection.trans.arr], Array modifications

          1 For any type T, for each function std::meta::MOD defined in this clause, std::meta::MOD(^T) returns the reflection of the corresponding type std::MOD_t<T> as specified in 21.3.8.5 [meta.trans.arr].

          -
          consteval info remove_extent(info type);
          -consteval info remove_all_extents(info type);
          +
          consteval info remove_extent(info type);
          +consteval info remove_all_extents(info type);

          5.2.8.5 [meta.reflection.trans.ptr], Pointer modifications

          1 For any type T, for each function std::meta::MOD defined in this clause, std::meta::MOD(^T) returns the reflection of the corresponding type std::MOD_t<T> as specified in 21.3.8.6 [meta.trans.ptr].

          -
          consteval info remove_pointer(info type);
          -consteval info add_pointer(info type);
          +
          consteval info remove_pointer(info type);
          +consteval info add_pointer(info type);

          5.2.8.6 [meta.reflection.trans.other], Other transformations

          @@ -2739,23 +2822,23 @@

          1 For any type T, for each function std::meta::MOD defined in this clause with signature std::meta::info(std::meta::info), std::meta::MOD(^T) returns the reflection of the corresponding type std::MOD_t<T> as specified in 21.3.8.7 [meta.trans.other].

          2 For any pack of types T..., for each function std::meta::VARIADIC-MOD defined in this clause with signature std::meta::info(std::span<const std::meta::info>), std::meta::VARIADIC-MOD({^T...}) returns the reflection of the corresponding type std::VARIADIC-MOD_t<T...> as specified in 21.3.8.7 [meta.trans.other].

          3 For any type T and pack of types U..., std::meta::invoke_result(^T, {^u...}) returns the reflection of the corresponding type std::invoke_result_t<T, U...> (21.3.8.7 [meta.trans.other]).

          -
          consteval info remove_cvref(info type);
          -consteval info decay(info type);
          -consteval info common_type(span<const info> type_args);
          -consteval info common_reference(span<const info> type_args);
          -consteval info underlying_type(info type);
          -consteval info invoke_result(info type, span<const info> type_args);
          -consteval info unwrap_reference(info type);
          -consteval info unwrap_ref_decay(info type);
          +
          consteval info remove_cvref(info type);
          +consteval info decay(info type);
          +consteval info common_type(span<const info> type_args);
          +consteval info common_reference(span<const info> type_args);
          +consteval info underlying_type(info type);
          +consteval info invoke_result(info type, span<const info> type_args);
          +consteval info unwrap_reference(info type);
          +consteval info unwrap_ref_decay(info type);

          4 [Example:

          -
          // example implementation
          -consteval info unwrap_reference(info type) {
          -  if (has_template_arguments(type) && template_of(type) == ^reference_wrapper) {
          -    return add_lvalue_reference(template_arguments_of(type)[0]);
          -  } else {
          -    return type;
          -  }
          -}
          +
          // example implementation
          +consteval info unwrap_reference(info type) {
          +  if (has_template_arguments(type) && template_of(type) == ^reference_wrapper) {
          +    return add_lvalue_reference(template_arguments_of(type)[0]);
          +  } else {
          +    return type;
          +  }
          +}

          -end example]

          diff --git a/2996_reflection/reflection.md b/2996_reflection/reflection.md index 8834d745..2ca9cd3b 100644 --- a/2996_reflection/reflection.md +++ b/2996_reflection/reflection.md @@ -1007,10 +1007,9 @@ Although we believe a single opaque `std::meta::info` type to be the best and mo ```cpp // Represents a 'std::meta::info' constrained by a predicate. template - requires (type_of(reflect_invoke(Pred, {reflect_value(^int)})) == ^bool) + requires (type_of(^([:Pred:](^int))) == ^bool) struct metatype { - // The underlying 'std::meta::info' value. - const std::meta::info value; + std::meta::info value; // Construction is ill-formed unless predicate is satisfied. consteval metatype(std::meta::info r) : value(r) { @@ -1019,29 +1018,30 @@ struct metatype { } // Cast to 'std::meta::info' allows values of this type to be spliced. - consteval operator const std::meta::info&() const { return value; } + consteval operator std::meta::info() const { return value; } static consteval bool check(std::meta::info r) { return [:Pred:](r); } - - static consteval metatype make(std::meta::info r) { return {r}; } }; // Type representing a "failure to match" any known metatypes. -struct unmatched { static consteval unmatched make(...) { return {}; } }; +struct unmatched { + consteval unmatched(std::meta::info) {} + static consteval bool check(std::meta::info) { return true; } +}; // Returns the given reflection "enriched" with a more descriptive type. template -constexpr auto enrich = [](std::meta::info r) consteval { - std::array checks = {^Choices::check...}; - std::array ctors = {^Choices::make...}; - - auto choice = ^unmatched; - for (auto [check, make] : std::views::zip(checks, makes)) - if (value_of(reflect_invoke(check, {reflect_value(r)}))) { - choice = make; - break; - } - return reflect_invoke(choice, {reflect_value(r)}); +consteval std::meta::info enrich(std::meta::info r) { + std::array ctors = {members_of(^Choices, std::meta::is_constructor)[0]..., + members_of(^unmatched, std::meta::is_constructor)[0]}; + std::array checks = {^Choices::check..., ^unmatched::check}; + + std::meta::info choice; + for (auto [check, ctor] : std::views::zip(checks, ctors)) + if (value_of(reflect_invoke(check, {reflect_value(r)}))) + return reflect_invoke(ctor, {reflect_value(r)}); + + std::unreachable(); } ``` ::: @@ -1059,14 +1059,15 @@ void PrintKind(fn_t) { std::println("function"); } void PrintKind(unmatched) { std::println("unknown kind"); } int main() { - constexpr auto enrich = ::enrich; + // Classifies any reflection as one of: Type, Function, or Unmatched. + auto enrich = [](std::meta::info r) { return ::enrich(r); }; // Demonstration of using 'enrich' to select an overload. - Printer::PrintKind([:enrich(^int):]); // "type" - Printer::PrintKind([:enrich(^main):]); // "function" - Printer::PrintKind([:enrich(^3):]); // "unknown kind" -} + PrintKind([:enrich(^main):]); // "function" + PrintKind([:enrich(^int):]); // "type" + PrintKind([:enrich(^3):]); // "unknown kind" ``` +::: Note that the `metatype` class can be generalized to wrap values of any literal type, or to wrap multiple values of possibly different types. This has been used, for instance, to select compile-time overloads based on: whether two integers share the same parity, the presence or absence of a value in an `optional`, the type of the value held by a `variant` or an `any`, or the syntactic form of a compile-time string. @@ -1440,6 +1441,9 @@ namespace std::meta { // @[substitute](#substitute)@ consteval auto substitute(info templ, span args) -> info; + // @[reflect_invoke](#reflect_invoke)@ + consteval auto reflect_invoke(info target, span args) -> info; + // @[value_of](#value_oft)@ template consteval auto value_of(info) -> T; @@ -1490,9 +1494,6 @@ namespace std::meta { template consteval auto reflect_value(T value) -> info; - // @[reflect_invoke](#reflect_invoke)@ - consteval auto reflect_invoke(info target, span args) -> info; - // @[define_class](#data_member_spec-define_class)@ struct data_member_options_t; consteval auto data_member_spec(info class_type, @@ -1676,6 +1677,22 @@ typename[:r:] si; // Error: T::X is invalid for T = int. ``` ::: +### `reflect_invoke` + +:::bq +```c++ +namespace std::meta { + consteval auto reflect_invoke(info target, span args) -> info; +} +``` +::: + +This metafunction produces a reflection of the value returned by a call expression. + +Letting `F` be the entity reflected by `target`, and `A_0, ..., A_n` be the sequence of entities reflected by the values held by `args`: if the expression `F(A_0, ..., A_N)` is a well-formed constant expression evaluating to a type that is not `void`, and if every value in `args` is a reflection of a constant value, then `reflect_invoke(target, args)` evaluates to a reflection of the constant value `F(A_0, ..., A_N)`. + +For all other invocations, `reflect_invoke(target, args)` is ill-formed. + ### `value_of` :::bq @@ -1743,23 +1760,6 @@ namespace std::meta { This metafunction produces a reflection representing the constant value of the operand. -### `reflect_invoke` - -:::bq -```c++ -namespace std::meta { - consteval auto reflect_invoke(info target, span args) -> info; -} -``` -::: - -This metafunction produces a reflection of the value returned by a call expression. - -Letting `F` be the entity reflected by `target`, and `A_0, ..., A_n` be the sequence of entities reflected by the values held by `args`: if the expression `F(A_0, ..., A_N)` is a well-formed constant expression evaluating to a type that is not `void`, and if every value in `args` is a reflection of a constant value, then `reflect_invoke(target, args)` evaluates to a reflection of the constant value `F(A_0, ..., A_N)`. - -For all other invocations, `reflect_invoke(target, args)` is ill-formed. - - ### `data_member_spec`, `define_class` :::bq From 640111fe2590252ef12d82051ce26d25ff226114 Mon Sep 17 00:00:00 2001 From: Barry Revzin Date: Wed, 14 Feb 2024 08:14:43 -0600 Subject: [PATCH 3/3] Adding a note --- 2996_reflection/p2996r2.html | 29 ++++++++++++++++------------- 2996_reflection/reflection.md | 3 +++ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/2996_reflection/p2996r2.html b/2996_reflection/p2996r2.html index ca2a1a20..39febf0f 100644 --- a/2996_reflection/p2996r2.html +++ b/2996_reflection/p2996r2.html @@ -4,7 +4,7 @@ - + Reflection for C++26