Daveed Vandevoorde wrote:Hello reflecting friends,
(And Happy New Year!)
I’ve uploaded on the 2022 Teleconferences wiki for SG-7 an updated version
of P1240 (which has also been submitted for the next mailing).
Changes include:
— The syntax agreements that SG-7 voted through P2320 (e.g., prefix ^
instead of reflexpr(…) to create reflections)
— The proposed metafunctions now use span and string_view instead
of vector and string
— Various corrections and clarifications based on reader feedback and
implementation experience
I think that it might be worth discussing Matus's alternative representationof reflections. Roughly, while P1240 has something equivalent tostruct info { uintptr_t id; };Matus instead suggests (if I understand correctly)template<uintptr_t id> struct info {};This implies that one does constexpr info x = ^T;under P1240 and constexpr auto x = ^T;under the hypothetical alternative.The arguments against this are (again IIUC) performance - the lattercauses much more template instantiations because functions are like consteval some_func( auto refl );instead of consteval some_func( info refl );However, there's a difference in usability, too. In the first case, one canalways splice [:refl:] inside some_func (because it's an instantiation context),whereas in the second case, one cannot.
That’s a massive price to pay, though (_every_ reflection value costs a type instance). And if you’re willing to pay it, you can with P1240. You can write:
template<info R> struct Refl { ... };
and now you have that same capability.
The reverse is not true.
(You could also write:
template<info refl> consteval some_func();
which is slightly cheaper in terms of compilation resources and different notation.)
Daveed
There is also the matter of a) iteration, and b) a more serious performance reason I have not seen mentioned elsewhere.
Iteration: special iteration mechanisms are required, unless you want to require a functional style as Matus’s implementation uses: see Andrew/Wyatt/Lock3’s `template for(auto …)` or `for(constexpr auto …)`, which instantiates each iteration. Non-range-based iteration (while statements, non-range for statements etc.) are almost certainly not feasible.
(Aside: for those interested in trying the `template for` with Matus’s implementation or other template-instantiation based implementations, I extracted it into a clean branch here:
And a merge of this with Matus’s implem and my string injection implem for code generation is her:
though it is a bit behind Matus’s.)
More serious performance reason:
First, suppose the reflection library included a queries for for fetching other declarations in the TU: say, it's sibling declarations and parent/children.
```
std::meta::get_next_decl(auto r)
std::meta::get_prev_decl(auto r)
std::meta::get_parent_decl(auto r)
std::meta::get_first_child_decl(auto r)
```
Now suppose a user decides they want to interface with reflection in an object-oriented style:
```
template<auto R>
struct DeclWrapper {
auto nextDecl() { return DeclWrapper <decltype(std::meta::get_next_decl(R))>{}; }
auto prevDecl() {…}
auto getParent() { … }
…
};
#include … // lots of stuff, big translation unit
int foo;
constexpr auto foorefl = DeclWrapper <decltype(^foo)>()>();
```
What happens when `foorefl` is instantiated?
First each member of DeclWrapper<decltype(^foo)> must be instantiated, which means we have to instantiate each return type, each of which is a DeclWrapper, which itself has to be instantiated, …
The chain reaction results in instantiations of DeclWrappers for *every single declaration in the translation unit*.
It is probably easier to just figure out narrow solutions resolving the precise deficiencies of value-based implementation than dealing with these issues.