C++ Logo


Advanced search

Subject: Re: [isocpp-ext] P2320: "The Syntax of Static Reflection" feedback request
From: David Rector (davrec_at_[hidden])
Date: 2021-04-07 18:02:52

> On Apr 7, 2021, at 2:54 PM, René Ferdinand Rivera Morell via SG7 <sg7_at_[hidden]> wrote:
> On Wed, Apr 7, 2021 at 1:42 PM David Vandevoorde <daveed_at_[hidden] <mailto:daveed_at_[hidden]>> wrote:
> > On Apr 7, 2021, at 10:15 AM, René Ferdinand Rivera Morell via Ext <ext_at_[hidden] <mailto:ext_at_[hidden]>> wrote:
> >
> > First, sorry for the late reply, I blame slow paper reading :-) Can someone clarify something that I must be missing about the proposed splice syntax. Is there rationale for why one needs any splice syntax at all given that the reflections are strongly typed? At a, probably very ignorant, high level it appears to me that you could just have the reflected names without the "[:R:]". What am I missing?
> There are at least two reasons. First, in non-expression contexts it allows us to know that we should switch to an expression context. E.g.:
> struct D1
> : X::B { // Currently, “X::B” is assumed not to be an expression here.
> …
> };
> and the parallel:
> struct D2
> : [: X::R :] { // The operand of splicers are expressions. So X::R can be assumed to be an expression.
> …
> };
> Makes sense now :-) Although it would be possible to not assume it's not an expression and use the context. Which is what I was alluding to from my questions.
> Second, reflection-expression vs. spliced-expression disambiguation. Consider:
> consteval info f() { … }
> constexpr auto r1 = f();
> constexpr auto f2 = [: f() :];
> Suppose f() returns a reflection of an expression. Clear r1 and r2 mean different things and one shouldn’t be assumed to be equivalent to the other (r1 is initialized with the expression reflection itself; r2 is initialized with the expression reflected by that expression reflection (which itself may or may not be an expression reflection, or some other reflection, etc.).
> But that makes it clear why it's needed. No contextual way to disambiguate that without the splice operation. Thanks!

I wouldn’t give up so easily; the same issue Daveed raises (with auto-deduced return types) applies for references, but we have explicitly defined the semantics for to allow the user to distinguish:
int &f() {…}
auto j = f(); //j is an int
auto &jref = f(); //jref is an int&

The same rules could conceivably be applied by simply substituting ^ for &:
class A {};
consteval class^ getA() { return [:^A:]; } //returns a "reference to a class"
consteval auto^ getA2() { return getA(); } //okay
consteval auto error() { return getA(); } //error
class Foo : public getA2() {};

The analogy between reflection/splicing and pointers/dereferencing is extremely strong, such that the possibility of a "reference"-analog which handles the dereferencing automatically is still possible (assuming expressions can be parsed in arbitrary contexts, which I agree would seem to be possible.)

The only real possible downside is readability, which is important.

*But* let’s set the reference issue aside. This whole matter raises the much more important question: just why *aren’t* reflections strongly-typed? Why only meta::info? Why is the user not allowed to specify further type information? (Note this is *not* the same as whether to use object-oriented reflection — discussed in P1240 pp5-6, totally different issue, those arguments do not apply here.)

By adopting a universal meta::info, we are knee-capping the compiler, so that it cannot perform basic type checking on templates prior to instantiation:


That seems like a big issue. Among other things, that type checking has to be done upon each instantiation probably affects efficiency, which is the whole reason for `meta::info` in the first place.

I think adopting syntax analogous to pointers (and, arguably, references) should be considered. First step along these lines: change `meta::info` to `[:auto:]`, which is in turn analogous to `auto *` : we know it’s a reflection/pointer, but we won’t say any more about what kind of thing it reflects/points to. Same semantics as meta::info has currently.

But this leaves us space to support, now or in the future, more specific "pointer-like" reflection types: [:class:], [:template<typename>:], [:meta::expr<int>:] (reflection of an integer-typed expression), etc.

And this would in turn leave us the possibility of supporting "reference-like" analogs of those: class^, template<typename>^, meta::expr<int>^, should we wanted to allow users to do without splicing/reflecting, by instead allowing consteval expressions in arbitrary contexts, which as Rene notes seems possible.

I suppose this needs a paper. If anyone else has thoughts or has done work along these lines, please weigh in.

> --
> -- René Ferdinand Rivera Morell
> -- Don't Assume Anything -- No Supone Nada
> -- Robot Dreams - http://robot-dreams.net <http://robot-dreams.net/>
> --
> SG7 mailing list
> SG7_at_[hidden] <mailto:SG7_at_[hidden]>
> https://lists.isocpp.org/mailman/listinfo.cgi/sg7 <https://lists.isocpp.org/mailman/listinfo.cgi/sg7>

SG7 list run by sg7-owner@lists.isocpp.org

Older Archives on Google Groups