C++ Logo

sg7

Advanced search

Re: [SG7] P2320: "The Syntax of Static Reflection" feedback request

From: David Rector <davrec_at_[hidden]>
Date: Sun, 21 Feb 2021 15:54:43 -0500
> On Feb 21, 2021, at 11:46 AM, Ville Voutilainen <ville.voutilainen_at_[hidden]> wrote:
>
> On Sun, 21 Feb 2021 at 16:45, David Rector via SG7 <sg7_at_[hidden]> wrote:
>> But to be clear, the Sum<T, U> example, and others of that level of complexity, *definitely need to be supported*. If Andrew et al disagree, I think we deserve an explicit statement to that effect. There is a lot of functionality at stake here.
>
> Such things certainly should be supported. It's just another variant
> of a generic Mediator that invokes multiple
> Observers, and summing is just one of many things that one might want
> to do with such a thing, others
> include, for example, collecting the results in a tuple, and
> collecting the results into a vector. The generic Mediator
> has the same functions its Observers have, and it just injects those
> functions into its own interface by reflecting
> the interface(s) of the Observer(s). We write these things fairly
> often, and reflection+injection makes it significantly
> less boilerplatey and more generic. And *using* such a generic
> Mediator makes it very easy to write such combinations
> without the status quo of excessive boiler-plate programming.

Well said. Some leisurely thoughts expanding on this:

Note too that such a metafunction, properly implemented, would indeed make that design pattern /obsolete/ — all the user need do is #include the right header and they can type `a + b + …` to their heart’s content, blissfully ignorant of the meaning of a "Mediator" and an "Observer" and all the expertise and manual labor the previous generation needed to code such things.

Indeed, I would argue is that reflection + injection, properly implemented, might well be able to make *every* design pattern obsolete — if it qualifies as a "pattern" in any sense, that means it is some call to a metafunction, and the only question is whether the programmers themselves must serve as that "metafunction", or if they are allowed to just write that metafunction explicitly and grab a beer.

I would thus argue the best source for generating examples is thus any design pattern textbook: try to write each one as a metafunction, and thus render knowledge of it obsolete altogether. Or, walk through Scott Meyers excellent books, and try to put him out of business: render as many bits of advice as possible obsolete via metafunctions.

What is left at the end, will be *true* programming expertise, I suppose.

FWIW, this belief is why I spent a year awhile back pretty much just learning clang and implementing the most general possible alternative to Andrew’s, without much regard to syntax or cleanliness. The resulting ugly "glorified preprocessor"/string mixin approach is a standard by which I think Andrew’s or any other alternative must be measured, and I will never be okay with any alternative that is not as capable as it, no matter how many people I admire and respect line up on the other side.

But I would still very much welcome *improvements* on it that don’t sacrifice its capabilities — it is definitely not without its problems; e.g. recall Richard’s line that "a good metaprogram is semantic" — if indeed the "semantic" approach could be accomplished without losing the capabilities of the "syntactic" metaprogramming approach, I agree it is a big improvement, and thus Andrew’s endeavor to develop such a semantic approach is certainly worthwhile, though incomplete at this point.

The challenge Andrew et al will face, it seems to me, is that, to implement injection properly in things like Sum<T,U> and PreCalcdSum<T,U> and other still more complex examples, you really just need to be able to write multiple statements in "meta" space, complete with meta variable declarations, control flow statements, etc. — *in the midst of writing arbitrary code *in "non-meta" space — i.e. in the midst of writing expressions, or initializers, or base classes.

This means that, necessarily, any sufficiently capable metaprogramming syntax is going to look like two programs being written on top of one another, however you do it (putting the injected parts in quotes per the string mixin approach, or using lambdas with special expansion capabilities, or doing something else). I believe someone — Cleiton? — asked about the Turing-completeness of the injection facilities, which perhaps is precisely this issue.

Re the original syntax issues — [::] vs $ etc., ultimately they pale in comparison to the larger issue of "capabilities". E.g. if a user has to help the compiler out by writing `…[:reflpack:]` instead of `[:reflpack:]…`, but as compensation they never need to learn about Mediators and Observers, that is an acceptable bargain.

Received on 2021-02-21 14:54:47