diff --git a/2996_reflection/p2996r0.html b/2996_reflection/p2996r0.html index 06f002cb..42bfdd6f 100644 --- a/2996_reflection/p2996r0.html +++ b/2996_reflection/p2996r0.html @@ -570,7 +570,8 @@

Contents

@@ -618,7 +619,10 @@

This proposal is not intended to be the end-game as far as reflection and compile-time metaprogramming are concerned. Instead, we expect it will be a useful core around which more powerful features will be added incrementally over time. In particular, we believe that most or all the remaining features explored in P1240R2 and that code injection (along the lines described in [P2237R0]) are desirable directions to pursue.

Our choice to start with something smaller is primarily motivated by the belief that that improves the chances of these facilities making it into the language sooner rather than later.

-

1.1 Why a single opaque reflection type?

+

1.1 Notable Additions to P1240

+

While we tried to select a useful subset of the P1240 features, we also made a few additions and changes. Most of those changes are minor. For example, we added a std::meta::test_type interface that makes it convenient to use existing standard type predicates (such as is_class_v) in reflection computations.

+

One addition does stand out, however: We have added metafunctions that permit the synthesis of simple struct and union types. While it is not nearly as powerful as generalized code injection (see [P2237R0]), it can be remarkably effective in practice.

+

1.2 Why a single opaque reflection type?

Perhaps the most common suggestion made regarding the framework outlined in P1240 is to switch from the single std::meta::info type to a family of types covering various language elements (e.g., std::meta::variable, std::meta::type, etc.).

We believe that doing so would be mistake with very serious consequences for the future of C++.

Specifically, it would codify the language design into the type system. We know from experience that it has been quasi-impossible to change the semantics of standard types once they were standardized, and there is no reason to think that such evolution would become easier in the future. Suppose for example that we had standardized a reflection type std::meta::variable in C++03 to represent what the standard called “variables” at the time. In C++11, the term “variable” was extended to include “references”. Such an change would have been difficult to do given that C++ by then likely would have had plenty of code that depended on a type arrangement around the more restricted definition of “variable”. That scenario is clearly backward-looking, but there is no reason to believe that similar changes might not be wanted in the future and we strongly believe that it behooves us to avoid adding undue constraints on the evolution of the language.

@@ -730,6 +734,7 @@

2. } } +

Note that this last version has lower complexity: While the versions using an expansion statement use an expected O(N) number of comparisons to find the matching entry, a std::map achieves the same with O(log(N)) complexity (where N is the number of enumerator constants).

2.5 Parsing Command-Line Options

Our next example shows how a command-line option parser could work by automatically inferring flags based on member names. A real command-line parser would of course be more complex, this is just the beginning:

@@ -776,7 +781,7 @@

#include <meta>
 
 template<typename... Ts> struct Tuple {
-  using storage = typename[:std::meta::synth_struct({make_nsdm_field(^T)...}):];
+  using storage = typename[:std::meta::synth_struct({nsdm_description(^T)...}):];
   storage data;
 
   Tuple(): data{} {}
@@ -802,12 +807,12 @@ 

2.7 Struct to Struct of Arrays

consteval auto make_struct_of_arrays(std::meta::info type, size_t n) -> std::meta::info {
-  std::vector<info> members;
+  std::vector<info> new_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)}));
+    new_members.push_back(nsdm_description(array_type, {.name = name_of(member)}));
   }
-  return std::meta::synth_struct(members);
+  return std::meta::synth_struct(new_members);
 }
 
 template <typename T, size_t N>
@@ -966,6 +971,7 @@ 

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

A quality implementation should emit the diagnostic text associated with an invalid reflection when attempting to splice that invalid reflection.

+

(This proposal does not at this time propose range-based splicers as described in P1240. We still believe that those are desirable. However, they are more complex to implement and they involve syntactic choices that benefit from being considered along with other proposals that introduce pack-like constructs in non-template contexts. Meanwhile, we found that many very useful techniques are enabled with just the basic splicers presented here.)

3.3 std::meta::info

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

@@ -1146,7 +1152,7 @@

3 }

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

-

3.4.10 make_nsdm_field, synth_struct, synth_union

+

3.4.10 nsdm_description, synth_struct, synth_union

namespace std::meta {
   struct nsdm_field_args {
@@ -1155,20 +1161,20 @@ 

optional<int> width; }; - consteval auto make_nsdm_field(info type, nsdm_field_args args = {}) -> info; + consteval auto nsdm_description(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.

+

nsdm_description creates a reflection that describes a non-static data member 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. Note that the reflection obtained from nsdm_description is not the reflection of a non-static data member itself; it only encapsulates the information needed to synthesize such a data member. In particular, metafunctions like name_of, type_of, and parent_of are not applicable to the result of an nsdm_description.

+

synth_struct and synth_union take a range of NSDM descriptions and return a reflection that denotes a struct and union, respectively, comprised of corresponding 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),
+  nsdm_description(^int),
+  nsdm_description(^char),
+  nsdm_description(^double),
 });
 
 // T is a reflection of the type
@@ -1179,8 +1185,8 @@ 

// } constexpr auto U = std::meta::synth_struct({ - make_nsdm_field(^int, {.name="i", .align=64}), - make_nsdm_field(^int, {.name="j", .align=64}), + nsdm_description(^int, {.name="i", .align=64}), + nsdm_description(^int, {.name="j", .align=64}), }); // U is a reflection of the type