diff --git a/2996_reflection/p2996r0.html b/2996_reflection/p2996r0.html index 22c5810e..125cb270 100644 --- a/2996_reflection/p2996r0.html +++ b/2996_reflection/p2996r0.html @@ -580,8 +580,9 @@
This example uses a “magic” std::meta::synth_struct
template along with member reflection through the members_of
metafunction to implement a std::tuple
-like type without the usual complex and costly template metaprogramming tricks that that involves when these facilities are not available.
+++consteval auto make_struct_of_arrays(std::meta::info type, size_t n) -> std::meta::info { + std::vector<info> members; + for (std::meta::info member : members_of(type, std::meta::is_nonstatic_data_member)) { + auto array_type = substitute(^std::array, {type_of(member), reflect_value(n)}); + members.push_back(make_nsdm_field(array_type, {.name = name_of(member)})); + } + return std::meta::synth_struct(members); +} + +template <typename T, size_t N> +using struct_of_arrays = [: make_struct_of_arrays(^T, N) :];
Example:
++ ++
This example is taken from Boost.Describe, translated to using std::format
instead of iostreams:
--+struct universal_formatter { - constexpr auto parse(auto& ctx) { return ctx.begin(); } - - template <typename T> - auto format(T const& t, auto& ctx) const { - auto out = ctx.out(); - *out++ = '{'; - - auto delim = [first=true]() mutable { - if (!first) { - *out++ = ','; - *out++ = ' '; - } - first = false; - }; - - template for (constexpr auto base : bases_of(^T)) { - delim(); - out = std::format_to(out, "{}", static_cast<[:base:] const&>(t)); - } - - template for (constexpr auto mem : members_of(^T, std::meta::is_nonstatic_data_member)) { - delim(); - out = std::format_to(out, ".{}={}", name_of(mem), t.[:mem:]); - } - - *out++ = '}'; - return out; - } -}; - -struct X { int m1 = 1; }; -struct Y { int m2 = 2; }; -class Z : public X, private Y { int m1 = 3; int m2 = 4; }; - -template <> struct std::formatter<X> : universal_formatter { }; -template <> struct std::formatter<Y> : universal_formatter { }; -template <> struct std::formatter<Z> : universal_formatter { }; - -int main() { - std::println("{}", Z()); // {{.m1 = 1}, {.m2 = 2}, .m1 = 3, .m2 = 4} -}
struct universal_formatter { + constexpr auto parse(auto& ctx) { return ctx.begin(); } + + template <typename T> + auto format(T const& t, auto& ctx) const { + auto out = ctx.out(); + *out++ = '{'; + + auto delim = [first=true]() mutable { + if (!first) { + *out++ = ','; + *out++ = ' '; + } + first = false; + }; + + template for (constexpr auto base : bases_of(^T)) { + delim(); + out = std::format_to(out, "{}", static_cast<[:base:] const&>(t)); + } + + template for (constexpr auto mem : members_of(^T, std::meta::is_nonstatic_data_member)) { + delim(); + out = std::format_to(out, ".{}={}", name_of(mem), t.[:mem:]); + } + + *out++ = '}'; + return out; + } +}; + +struct X { int m1 = 1; }; +struct Y { int m2 = 2; }; +class Z : public X, private Y { int m1 = 3; int m2 = 4; }; + +template <> struct std::formatter<X> : universal_formatter { }; +template <> struct std::formatter<Y> : universal_formatter { }; +template <> struct std::formatter<Z> : universal_formatter { }; + +int main() { + std::println("{}", Z()); // {{.m1 = 1}, {.m2 = 2}, .m1 = 3, .m2 = 4} +}
Based on the [N3980] API:
- +
^
)Attempting to splice a reflection value that does not meet the requirement of the splice (including “invalid reflections”) is ill-formed. For example:
- +
A quality implementation should emit the diagnostic text associated with an invalid reflection when attempting to splice that invalid reflection.
std::meta::info
The type std::meta::info
can be defined as follows:
- +
In our initial proposal a value of type std::meta::info
can represent:
Notably absent at this time are general non-constant expressions (that aren’t expression-ids referring to variables or structured bindings). For example:
- +
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:
- +
Namespace std::meta
is associated with type std::meta::info
: That allows the core meta functions to be invoked without explicit qualification. For example:
- +
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.
invalid_reflection
, is_invalid
, diagnose_error
- +
An invalid reflection represents a potential diagnostic for an erroneous construct. Some standard metafunctions will generate such invalid reflections, but user programs can also create them with the invalid_reflection
metafunction. is_invalid
returns true if it is given an invalid reflection. Evaluating diagnose_error
renders a program ill-formed. If the given reflection is for an invalid reflection, an implementation is encouraged to render the encapsulated message and source position as part of the diagnostic indicating that the program is ill-formed.
name_of
, display_name_of
, source_location_of
- +
Given a reflection r
that designates a declared entity X
, name_of(r)
returns a string_view
holding the unqualified name of X
. 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.
type_of
, parent_of
, entity_of
- +
If r
is a reflection designating a typed entity, type_of(r)
is a reflection designating its type. Otherwise, type_of(r)
produces an invalid reflection.
If r
designates a member of a class or namespace, parent_of(r)
is a reflection designating its immediately enclosing class or namespace. Otherwise, parent_of(r)
produces an invalid reflection.
If r
designates an alias, entity_of(r)
designates the underlying entity. Otherwise, entity_of(r)
produces r
.
members_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 enumerators_of(info class_type, Fs ...filters) -> vector<info>; - template<typename ...Fs> - consteval auto subobjects_of(info class_type, Fs ...filters) -> vector<info>; -}
namespace std::meta { + template<typename ...Fs> + consteval auto members_of(info class_type, Fs ...filters) -> vector<info>; + template<typename ...Fs> + consteval auto enumerators_of(info class_type, Fs ...filters) -> vector<info>; + template<typename ...Fs> + consteval auto subobjects_of(info class_type, Fs ...filters) -> vector<info>; +}
substitute
- +
Given a reflection for a template and reflections for template arguments that match that template, substitute
returns a reflection for the entity obtains by substituting the given arguments in the template.
For example:
- +
This process might kick off instantiations outside the immediate context, which can lead to the program being ill-formed. Substitution errors in the immediate context of the template result in an invalid reflection being returned.
Note that the template is only substituted, not instantiated. For example:
- +
entity_ref<T>
, value_of<T>
, ptr_to_member<T>
- +
If r
is a reflection of a variable or function of type T
, entity_ref<T>(r)
evaluates to a reference to that variable or function. Otherwise, entity_ref<T>(r)
is ill-formed.
If r
is a reflection for a constant-expression or a constant-valued entity of type T
, value_of(r)
evaluates to that constant value. Otherwise, value_of<T>(r)
is ill-formed.
test_type<Pred>
- +
This utility translates existing metaprogramming predicates (expressed as constexpr variable templates) to the reflection domain. For example:
- +
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.
-+namespace std::meta { - 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_deleted(info entity) -> bool; - consteval auto is_defaulted(info entity) -> bool; - consteval auto is_explicit(info entity) -> bool; - consteval auto is_override(info entity) -> bool; - consteval auto is_pure_virtual(info entity) -> bool; - consteval auto has_static_storage_duration(info r) -> bool; - - consteval auto is_nsdm(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_static(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 has_template_arguments(info r) -> bool; -}
namespace std::meta { + 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_deleted(info entity) -> bool; + consteval auto is_defaulted(info entity) -> bool; + consteval auto is_explicit(info entity) -> bool; + consteval auto is_override(info entity) -> bool; + consteval auto is_pure_virtual(info entity) -> bool; + consteval auto has_static_storage_duration(info r) -> bool; + + consteval auto is_nsdm(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_static(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 has_template_arguments(info r) -> bool; +}
reflect_value
- +
This metafunction produces a reflection representing the constant value of the operand.
make_nsdm_field
, synth_struct
, synth_union
-+namespace std::meta { - struct nsdm_field_args { - optional<string_view> name; - optional<int> alignment; - optional<int> width; - }; - - consteval auto make_nsdm_field(info type, nsdm_field_args args = {}) -> info; - - consteval auto synth_struct(span<info const>) -> info; - consteval auto synth_union(span<info const>) -> info; -}
namespace std::meta { + struct nsdm_field_args { + optional<string_view> name; + optional<int> alignment; + optional<int> width; + }; + + consteval auto make_nsdm_field(info type, nsdm_field_args args = {}) -> info; + + consteval auto synth_struct(span<info const>) -> info; + consteval auto synth_union(span<info const>) -> info; +}
make_nsdm_field
creates a reflection of a non-static data member field of given type. Optional alignment, bit-field-width, and name can be provided as well. If type
does not designated a valid data member type, an invalid reflection is produced. If no name
is provided, the name of the non-static data member is unspecified.
synth_struct
and synth_union
take a range of NSDM fields and return a reflection that denotes a struct and union, respectively, comprised of those non-static data members.
For example:
-+constexpr auto T = std::meta::synth_struct({ - make_nsdm_field(^int), - make_nsdm_field(^char), - make_nsdm_field(^double), -}); - -// T is a reflection of the type -// struct { -// int _0; -// char _1; -// double _2; -// } - -constexpr auto U = std::meta::synth_struct({ - make_nsdm_field(^int, {.name="i", .align=64}), - make_nsdm_field(^int, {.name="j", .align=64}), -}); - -// U is a reflection of the type -// struct { -// alignas(64) int i; -// alignas(64) int j; -// }
constexpr auto T = std::meta::synth_struct({ + make_nsdm_field(^int), + make_nsdm_field(^char), + make_nsdm_field(^double), +}); + +// T is a reflection of the type +// struct { +// int _0; +// char _1; +// double _2; +// } + +constexpr auto U = std::meta::synth_struct({ + make_nsdm_field(^int, {.name="i", .align=64}), + make_nsdm_field(^int, {.name="j", .align=64}), +}); + +// U is a reflection of the type +// struct { +// alignas(64) int i; +// alignas(64) int j; +// }
- +