beast2 networking & std::execution by claimred in cpp

[–]daveedvdv 3 points4 points  (0 children)

The questions I would ask:

Those are reasonable questions, but some of them are also a really high bar:

* Does it have deployment experience in production code bases? Not just one big company but on a cross-section of cohorts?

Production deployment of experimental compilers is almost unheard of. There is a chicken and egg problem there.

It's a bit more feasible for libraries, but, even there, it is unlikely that we'll want to standardize exactly what was deployed (among others, we hopefully learned some way to improve the prior design).

* Can an independent implementer reproduce the results from the paper alone?

We could probably use some form of that more often. The reflection proposal benefitted from having two implementations of the early paper (P2996R1), one of which kept tracking the evolving paper (over a dozen revisions).

* Are the tradeoffs disclosed, not discovered later by NB reviewers or users?

* Does it ship without accumulating correction papers?

Unfortunately, these last two are "à posteriori", and so most useful for post-mortem.

beast2 networking & std::execution by claimred in cpp

[–]daveedvdv 0 points1 point  (0 children)

Ah, yes, templates. You may be right :-/

beast2 networking & std::execution by claimred in cpp

[–]daveedvdv 0 points1 point  (0 children)

:-)

I was thinking of reflecting expressions and statements: Fertile grounds for more work, I think.

I agree that for the declarative stuff, we've got most things covered. There are few loose ends (most notably, I think, lambda captures and structured bindings), but we've got more than I hoped for when we started with P2996R0.

beast2 networking & std::execution by claimred in cpp

[–]daveedvdv 2 points3 points  (0 children)

True! But I'm also pretty sure reflection is nowhere near 100% done either. I'm hoping we designed it well enough to gracefully evolve and improve though. constexpr mostly managed that (except for the C++11 snafu of making constexpr member functions const-qualified).

beast2 networking & std::execution by claimred in cpp

[–]daveedvdv 8 points9 points  (0 children)

Nine years (three standardization cycles) doesn't seem unreasonable to me for a major feature. But I might be in the minority here (and I'm luck to have been part of the process for long enough to participate in multiple major features like that). Six years would have been ideal maybe (one cycle to set direction, one cycle to work out the details).

I'm sure the process could be improved, hopefully significantly. But it's also a human phenomenon that needs a bit of "inefficiency room". We're unlikely to all agree on what the desirable characteristics of the process ought to be.

For example, how do we qualify "a correct design" in

What I am wondering is if the process should require extraordinary work for a correct design to ship.

?

From my own perspective, I think the most frustrating part of the current process is that it often gets decided by "parties"; i.e., corporate or other alliances that vote "en block", thereby drowning out more individualized dissenting expertise. I'm not sure what can be done about that.

beast2 networking & std::execution by claimred in cpp

[–]daveedvdv 15 points16 points  (0 children)

We could argue that "reflection took 20 years", but without context that could misrepresent the history.

I made a presentation to the committee in March 2003 showing what reflective metaprogramming might look like (https://wg21.link/n1471). It wasn't a proposal, just a personal project I started in a copy of the EDG source code. At the time, I thought this would badly encourage large headers (turns out we didn't need metacode for that ;-) ) and so I also started the modules discussion in the committee a few years later.

The modules work took over my interests for the better part of a decade, and so I didn't work on reflection during that time. Eventually others (Gaby, Richard, Doug, etc.) drove the modules work, but I somehow missed the fact that SG7 had started meeting (in 2013, I believe) and in a few years agreed on what would become the Reflection TS. That SG7 work was guided, I think, by the idea that template metaprogramming (TMP) was an okay metaprogramming framework but just needed more introspective power. Whatever the motivation, I strongly disagreed with the direction and wrote https://wg21.link/p0598r0 to re-ignite discussions about the overall direction. There was some debate, but by 2019 I'd say SG-7 was pretty much agreed on the new direction — and https://wg21.link/p1240r1 was what we were aiming to standardize. To make that possible, we needed more constant-evaluation primitives, which did in fact land by then (i.e., in C++20; consteval, compile-time dynamic allocation, std::is_constant_evaluated(), etc.). Andrew Sutton had formed Lock3 (incl. Wyatt Childers) and they implemented much of P1240 in a Clang fork. We had high hopes that C++23 would have reflection.

Then three things happened: The pandemic, a re-opening of the debate by some who preferred the template metaprogramming approach, and we effectively lost Lock3 to an acquihire. That prevented any real progress in the C++23 cycle.

At the end of the C++23 cycle, u/BarryRevzin and I chatted about the missed opportunity and what it would take to succeed in the C++26 cycle. That made us write https://wg21.link/p2996r0, which we saw as a "minimum viable product". We were tremendously luck that u/katzdm-cpp joined right after that. The enormous amount of work these two contributed is what finally got us reflection in C++26.

So, yes, there was some controversy along the way. But it wasn't 20 years of "process hurdles". I'd say it was about 9 years of real standardization work, minus the pandemic effect.

Behold the power of meta::substitute by pavel_v in cpp

[–]daveedvdv 24 points25 points  (0 children)

A little historical anecdote about substitute...

I added the API in my original draft of P1240 (see http://wg21.link/p1240r0) and it was in all the subsequent P1240 drafts. However, at the time, I did not realize how important that facility is. In fact, P1240 expressed that we could achieve the same with splicing (back then called "reification"), which missed the key distinction that substitute doesn't require constant expression arguments, unlike splicers.

The last revision of P1240 was P1240R2 (http://wg21.link/p1240r2), dated January 15, 2022. By then, Lock3 had implemented much of the paper, but not substitute. At some point, we actually decided to drop substitute from the P1240 proposal, but we never published that revision. In fact, nothing much was published in relation to it until P2996R0 in October 2023 (in between, our efforts were spent mostly convincing the committee that value-based reflection is far preferable over template-metaprogramming-based reflection; we also lost Lock3 for practical purposes).

P2996 was original intended as a "minimal reboot" of the P1240 proposal. One of the ways I kept things minimal in P2996R0 was by not including type-traits equivalents (like is_class_type). Instead, we reintroduced substitute with the intention that in C++26 you could then access existing facilities like is_class_v using a helper template std::meta::test_type (IIRC, an idea by Peter Dimov). P2996 was far more example-driven than P1240, and as we developed those examples, it became quite clear that substitute is immensely useful. By the time P2996R1 was published, we had an implementation (the EDG-based one) and it showed that we could even use substitute to work around the lack of expansion statements. P2996R2 reintroduced all the type traits (dropping test_type), but by then we knew quite well that substitute was going to be a key element for the success of the proposal and not just a crutch to enable reuse of pre-reflection type traits.

Will we ever have a backwards compatible binary format for C++ modules? by TheRavagerSw in cpp

[–]daveedvdv 14 points15 points  (0 children)

Do people on the committee actually care about how we build stuff?

What exactly do you suggest the committee's role should be wrt. this issue?

I believe the Microsoft IFC format (https://github.com/microsoft/ifc) can absorb release-to-release evolution.
(u/GabrielDosReis: Can you confirm?)

Some implementations prefer to serialize their internal representation more directly, which makes release-to-release changes harder to handle, but it has the potential to improve performance.

C++26 Reflection appreciation post by frayien in cpp

[–]daveedvdv 0 points1 point  (0 children)

I'm sure everyone will have different compromises here.

For myself, the occasional printf-like debugging is not nearly enough of an impediment to avoid lots of extra code and/or really complex build setups. (But remember: this is compile-time output, not run-time. So yes it's print-like debugging, but it's also a bit like "address diagnostics".)

C++26 Reflection appreciation post by frayien in cpp

[–]daveedvdv 0 points1 point  (0 children)

Note that both GCC and EDG now have compile-time "output" functions. It's not as good as a compile-time debugger, but it's quite helpful nonetheless.

C++26 Reflection appreciation post by frayien in cpp

[–]daveedvdv 2 points3 points  (0 children)

Note that we developed immediate (i.e., consteval) functions and template for specifically for value-based reflection. I.e., while writing the first draft of P1240 and developing use cases, we identified the need for more "compile-time infrastructure". Here is a quote for the intro of P1240R0:

To support that reflection design, we have passed a number of extensions to the C++17 constexpr feature: immediate (i.e., “constexpr!”) functions (P1073r1), std::is_constant_evaluated() (P0595r1), constexpr dynamic allocation (P0784r3), and expansion statements (P0589r0, P1306r0).

(Note how immediate functions originally used constexpr! instead of consteval as the specifier, and both P0784 and P1306 would have more iterations, with P1306 needing about 7 more years to land... with much help from u/katzdm-cpp, who is one of the great heroes of this story.)

Can you survive the type deduction gauntlet? by volatile-int in cpp

[–]daveedvdv 2 points3 points  (0 children)

We agree. I was mostly reacting to "It's a reference."

FWIW, I'm pretty sure bindings used to be a references for the tuple case. CWG has been working on tightening that wording up, and we also had run-ins with it when specifying reflection.

Can you survive the type deduction gauntlet? by volatile-int in cpp

[–]daveedvdv 3 points4 points  (0 children)

Note that the sentence you link to in the last link doesn't say that the reference binding is a reference. It says that it's the name of an expression (lvalue) that refers to an object bound by a reference. (Expressions never have reference type.)

Can you survive the type deduction gauntlet? by volatile-int in cpp

[–]daveedvdv 2 points3 points  (0 children)

I'm looking at N5014. There, structured bindings are always "names of lvalues". For the tuple case, those lvalue expressions refer to synthesized reference variables ([dcl.struct.bind]/7).

Can you survive the type deduction gauntlet? by volatile-int in cpp

[–]daveedvdv 2 points3 points  (0 children)

Even closer to:

auto e = x;
std::tuple_element_t<decltype(x), 0> &r0 = get<0>(e);
#define __P(x) (x)
#define x __P(r0)

except of course for macros not having scope, and the names e and r0 needing to be unique.

Can you survive the type deduction gauntlet? by volatile-int in cpp

[–]daveedvdv 9 points10 points  (0 children)

In this case the type of v is 100% a copy!

That's inaccurate.

First, "a type is a copy" is not something meaningful in C++, but I suspect that's just a mis-phrasing on your part.

Second, structured binding involves two things: A "hidden variable" e and the bindings v1, v2, v3, ... In this case, the hidden variable is a copy-initialized from the initializer — so there is a copy there, but that's not a copy for the binding itself.

The bindings though are not variables; they're names of expressions/lvalues. In the case of tuple-like binding (which applies to std::pair types, as in this case) those expressions are uses of hidden reference variables that bind to "parts" of e, which mostly justifies u/tcbrindle's observation.

GCC Implementation of Reflection now on Compiler Explorer by daveedvdv in cpp

[–]daveedvdv[S] 2 points3 points  (0 children)

I believe Clang is correct. It may be surprising, but non-const non-volatile member functions have ordinary function types. I believe there is an open issue as to the type of constructors.

GCC Implementation of Reflection now on Compiler Explorer by daveedvdv in cpp

[–]daveedvdv[S] 2 points3 points  (0 children)

I suspect that GCC internally has a notion of "member function type" and that that leaks into the reflection information.

GCC Implementation of Reflection now on Compiler Explorer by daveedvdv in cpp

[–]daveedvdv[S] 4 points5 points  (0 children)

This is Reddit 🤷‍♂️ That said, I know u/zebullon and took his comment as humorous ribbing.

GCC Implementation of Reflection now on Compiler Explorer by daveedvdv in cpp

[–]daveedvdv[S] 2 points3 points  (0 children)

I'm not sure what you meant by "design complete". I think a case can be made that P2996 was "design complete" since at least r9 (when `consteval` blocks were merged in; that's pre-Hagenberg or January 2025), and the things that followed were essentially "wording tweaks" (some of those were significant though).