That said, if you want a non-DOM framework, surprisingly Google already provides it. It's called Flutter and it has the option to use a canvas, no DOM. You can see a large complex example at
Go there and type in NYC. You'll see text and images popup over the earth, etc. You'll see a toolbar and menus and a status bar etc... Click settings. The stuff that appears is all canvas. Click the Data Layers icon. The stuff that appears as all canvas. I think they finally made the search input box an input element for the reasons above but the rest is canvas.
Also note that canvas based input is also why Google Docs has so much trouble with emoji and non-English input.
It's far from the only thing it has issues with; I've found obviously broken UI patterns within a couple clicks of opening Earth's map view:
- right click doesn't appear to work anywhere (except input boxes), even on elements that have direct equivalents in other Google sites (like the account switcher),
- when you click the account switcher, as long as its open, the rest of the site ignores the mouse events; attempting to drag the map screen doesn't close the switcher, hovering over buttons doesn't change the cursor etc.
Still, to directly answer the first question, I would hope even if there wasn't obvious performance improvements immediately, if folks want to work on this, I see no reason not to explore it. If we are lucky, we find improvements we didn't expect.
My understanding is that the basic copy-and-patch approach without any other optimizations doesn’t actually give that much. The difference between an interpreter running opcodes A,B,C and a JIT emitting machine code for opcode sequence A,B,C is very little - the CPU running the code will execute roughly the same instructions for both, the only difference is that the jit avoids doing an op dispatch between each op - but that’s already not that expensive due to jump threading in the interpreter. Meanwhile the JIT adds an extra possible cost of more work if you ever need to jump from JIT back to fallback interpreter.
But what the JIT allows is to codegen machine code corresponding to more specialized ops that wouldn’t be that beneficial in the interpreter (as more and smaller ops make it much worse for icaches and branch predictors). For example standard CPython interpreter ops do very frequent refcount updates, while the JIT can relatively easily remove some sequences of refcount increments followed by immediate decrements in the next op.
Or maybe I misunderstood the question, then in other words: in principle copy-and-patch’s code generation is quite simple, and the true benefits come from the optimized opcode stream that you feed it that wouldn’t have been as good for the interpreter.
This syntax is pretty weird even by C++ standards, and it's certainly the worst way to do reflection I've personally seen. But I guess it's good that C++ is finally getting some kind of reflection. Only a few decades late to the party but hey they got there in the end.
Really as C++ grows and evolves, I feel more and more that I'd rather use plain C than wrangle with all of the bizzarro choices C++ has made. C++ is more and more often just the wrong choice of language for anything I want to do. If I need compile-time reflection, I'll use a C# program to generate C code.
This isn't reflection though, it's just textual code generation. In a way, it has the same problem's Rust's macros have (and indeed I'd love to have this kind of reflection in Rust). As an example, if you're implementing some kind of automated serialization, given an input struct with several fields, how can you even just find out its size? Or at what offset each field lives? Or if the field can be naively memcpy'd? You can hardcode the values if you do lots of assumptions and restrictions, or you can re-implement the logic the compilers already also do - but IMO it does feel the cleanest to just ask the compiler at compile time, which is exactly what C++ reflection proposal does.
This syntax is pretty weird even by C++ standards, and it's certainly the worst way to do reflection I've personally seen. But I guess it's good that C++ is finally getting some kind of reflection. Only a few decades late to the party but hey they got there in the end.
Really as C++ grows and evolves, I feel more and more that I'd rather use plain C than wrangle with all of the bizzarro choices C++ has made. C++ is more and more often just the wrong choice of language for anything I want to do. If I need compile-time reflection, I'll use a C# program to generate C code.
> I do not have access yet to a true C++26 compiler. When C++26 arrive, we will have features such as ‘template for’ which are like ‘for’ loops, but for template metaprogramming. Meanwhile, I use a somewhat obscure ‘expand’ syntax.
In particular, regarding the most cryptic line:
[:expand(nonstatic_data_members_of(^^T, ctx)):] >> [&]<auto member>{
In fact, at least from what I know, `expand()` and this fancy use of `>>` aren't actually part of the standard (the standard proposal uses them as an temporary example in case other features didn't get in). The equivalent (I think) line with the C++26 approved features would be a bit more friendly:
template for (constexpr auto member : nonstatic_data_members_of(^^T, ctx)) {
So while it's still not the prettiest thing in the world and knowing C++, it surely will produce some eldritch metaprogramming abominations, the author's examples made it look slightly uglier than it actually is.
No, I'm not sure where you got this idea. Macros are a disjoint feature from reflection. Macros exist to let you implement DSLs and abstract over syntax.
Instead of taking a raw token stream of a struct, parsing it with `syn` (duplicating the work the compiler does later), generating the proper methods and carefully generating trait checks for the compiler to check in a later phase (for example, `#[derive(Eq)] struct S(u16)` creates an invisible never-called method just to do `let _: ::core::cmp::AssertParamIsEq<u16>;` so the compiler can show an error 20s after an incorrectly used macro finished), just directly iterate fields and check `field.type.implements_trait(Eq)` inside the derive macro itself.
That said, that's just wishful thinking - with how complex trait solving is, supporting injecting custom code in the middle of it (checking existing traits and adding new trait impls) might make compile time even worse, assuming it's even possible at all. It’s also not a clear perf win if a reflection function were to run on each instantiation of a generic type.
I think the cause of the public perception issue could be the variant of Wirth's law: the size of an average codebase (and its dependencies) might be growing faster than the compiler's improvements in compiling it?
Again, I was aware that they added image generation, just not how much of a deal it turned out to be. Think of it like me occasionally noticing merchandise and TV trailers for a new movie without realizing it became the new worldwide box office #1.
Awkwardly, I never heard of it until now. I was aware that at some point they added ability to generate images to the app, but I never realized it was a major thing (plus I already had an offline stable diffusion app on my phone, so it felt less of an upgrade to me personally). With so much AI news each week, feels like unless you're really invested in the space, it's almost impossible to not accidentally miss or dismiss some big release.