Thanks for this link. Between it and the main article, I have a much better sense for how compile time reflection will actually be useful. The syntax is bad enough to make my eyes bleed, but at least they'll bleed for a good reason I hope.
techjamie
> The syntax is bad enough to make my eyes bleed
That's on brand for C++ in general. It works well, but it's ugly as sin while doing it.
grg0
I am happy^^ that ^^ is our quote character. ^^
bestouff
I don't agree with you. Yes syntax is often awful but no it doesn't work that well. It's a minefield of Undefined Behavior.
pjmlp
While true, just like everyone on Rust land rarely thinks about configuring their projects without clippy, C and C++ developers should learn static analysers for C and eventually C++ exist since 1979.
It doesn't fix everything, yet it fixes more than those that don't use them at all.
I have been using such tools since mid-1990's when coding in either C or C++, and sadly it is still a discussion point regarding adoption.
layer8
The rationale in section 2.2 for the single std::meta::info type is interesting. While having a single “dynamic” type is fine insofar as any type error would occur at compile time anyway, I wonder if there wouldn’t be a way to maintain future backwards compatibility for finer-grained meta types, as long as the language elements they reflect remain backwards-compatible in the language as well. From first principles, one would think that it should be possible for the backwards compatibility of the reflection system to mirror the backwards compatibility of the reflected language. I’d be interested in the underlying reasons for why this isn’t the case, if any.
jandrewrogers
I am looking forward to compile-time reflection, C++ has unusually strong metaprogramming capabilities among systems languages and this will significantly bolster that.
C++ does not win any prizes for being the most aesthetically pleasing programming language but the new syntax around reflection is pretty ugly even by those standards.
rickstanley
Yeah. I was wondering if it would be feasible to have a superset language for C++, much like what TypeScript is for JavaScript. But I guess there's always the risk of bike-shedding hell.
paulddraper
Correct on all counts
112233
Oh noes, the binding of internal compiler state to STL will become even more severe with this. It started slowly. You needed exact magic std::initializer_list definition in your headers to match the code generated by the compiler, or the program would crash. RTTI (that was of little use to programs that tried to avoid standard library)
Now there are coroutines and this thing. I feel like it is bye-bye to the completely non-standard compliant practice of using C++ without all the standard library.
Where should embedded c++ projects go now that they are not welcome anymore?
jcelerier
> Where should embedded c++ projects go now that they are not welcome anymore?
I'm just using all of this on embedded platforms without issues - and can't wait to use reflection there either, it'll be ultra useful for ESP32 and Teensy projects we're working on
adrian17
The std::meta stuff are only ever running at compile time, so I don't think this would actually interfere much?
112233
now that a lot of std:: is constexpr, who knows how much of it will get pulled into compile time by std::meta stuff (I do not. I am asking). I know of no way of using different std libraries, one for compile time and one for target. Meaning, using this c++ variant without std library shipped with the compiler just got even impossiblier.
TuxSH
About everything in std::initializer_list is noexcept, and builtin wrappers (type traits, bit_cast etc etc.) are 0-cost.
You shouldn't try to avoid all of the stdlib, but rather vet what you use from it. Furthermore, stuff like LTO or -ffunction-sections -fdata-sections (+ --gc-section when linking) gets rid of unused symbols, as long as your toolchain provider compiles the C stdlib and C++ stdlib with these
> coroutines
Almost everything is customizable by user (including class-scoped operator new), is it not?
> Where should embedded c++ projects go now that they are not welcome anymore?
C++ is still very much suitable for embedded, and while I disagree with most of your post I agree that: 1) there should be a linker flag to wrap all std:: exceptions instead of using -Wl,-wrap for each individual exception when needed and 2) that embedded feels like a second-class citizen sometimes ("deprecating volatile" (reverted), #embed missing C++23 etc)
112233
Thank you! It is nice to see that others keep trying and using C++ for embedded. As to 'vetting' the use of std library — I do not see how to do it without making a fork (or writing own "stripped down" version from scratch).
E.g., you cannot simply decide not to use container allocator parameters, to reduce symbol sizes.
The point I tried to make is that you cannot reasonably use modern features of C++ with your own standard library — the amount of undocumented internal compiler details you have to guess and match exactly is becoming unmanageable.
And while -fno-rtti and -fno-exceptions still miraculously are supported by compilers (it is explicitly non-conforming C++ even in "freestanding" implementation, according to 16.4.2.5) and stop compiler from emitting references to std::, other features like allocators do not have an "off" switch.
The initializer_list is a good example. N4950 17.10 explicitly tells you there are multiple valid ways to implement it. If you guess incorrectly, your program will crash.
So current C++ standard library must be treated like part of the compiler implementation. It did not use to be like that.
pjmlp
Historical accident, as until 1998 there was hardly any C++ standard library.
There was the C standard library that could be consumed by C++ applications, the C++ frameworks specific to each compiler vendor that sadly are no longer a thing, and the C++ ARM de facto standard with only iostreams, being used as the initial discussion for what would become C++98, a decade later.
Many of C++ warts are related to this historical evolution, like how many variations of string, arrays and other collection classes do you want to use in a single project?
TuxSH
> As to 'vetting' the use of std library — I do not see how to do it without making a fork (or writing own "stripped down" version from scratch). E.g., you cannot simply decide not to use container allocator parameters, to reduce symbol sizes.
Yes, correct, what I meant is that you can still use many features like type_traits, bit_cast, initializer_list, span, array etc. Of course, std::string and friends are a bit no-no in very memory constrained cases.
> So current C++ standard library must be treated like part of the compiler implementation.
Indeed. Though even in C, compiler will assume C standard library is present unless you explicitly tell it not to (it optimizes calls to memcpy and will emit memset calls when initializing variables to 0)
theICEBeardk
I work on a c++23 embedded platform that is used in very small microprocessors up to embedded linux stuff. We ship stuff that needs to run constant and C was just too unsafe for us (C requires you to remember to call functions RAII saves you from that) so we switched 2 decades ago (before my time there).
C++23 is not abandoning embedded. Instead there is a tonne of misinformation around that is confusing people. You can easily tell what parts of the STD is available by looking up the concept of Freestanding, which is a legitimate part of the c++ standard which tells you what is absolutely safe to use in embedded. Usually some of the non-freestanding stuff is also safe to use.
Then what you do is add support for the ETL (github.com/ETLCPP/) which will help you by offering STD like classes for the parts that are not safe to use or just give you the std class wrapped in their namespace.
What we do is turn off RTTI and for now exceptions (most compilers let you do that with ease but we are looking at maybe using them in the future because of recent research indicating it would be possible and save us binary size at the same time) and you lean heavily into the constexpr side of things because anything you can get the compiler that is running nightly or on your PC to do rather than on the embedded system is just fine. We do not use coroutines so there I have no opinion.
Personally I am looking forward to the Reflection stuff because it is all compile time (and no that does not mean that your std on your pc somehow leaks illegal functions into your code constexpr/consteval is embedded safe) and it will allow me to make code that will be easier to debug than the recursive template expansions are now (stepping through a recursion is bad even if we strictly limit the depth of them but reflection will allow me to do an expansion into a flat set of ifs instead).
112233
Yes, let's everyone look up the "freestanding" concept in N4950 section 16.4.2.5, and take note how it requires both exceptions and RTTI. Is that what you meant by "C++23 is not abandoning embedded"?
Yes, compilers support turning off exceptions and RTTI, for a long time now. C++ language does not. They could have supported it, but have chosen to explicitly not, as seen in the above section from the standard.
Nothing the committee does seem to support embedded. I will be glad to be shown I'm wrong.
SleepyMyroslav
You are right. Its not the job of the committee to support anything. They just have to vote on proposals.
Imagine Linux devs would start worrying about what "C standard" wants xD.
In my industry (gamedev), folks never needed to worry about how non-standard our code is either. As long as it ships on relevant half-dozen platforms and gets job done, its fine. One does not get a Boy scout badge for being standard compliant.
binary132
In C++ if you’re not standards compliant you are running the risk of hidden UB big time. Maybe your compiler will reveal it to you, probably not. Good luck!
pjmlp
Same with C, no game developer has ever lost sleep over that.
In fact they rely on lots of hardware UB for some console tricks.
dodomodo
It's not that hard to just copy the relevant part from the standard library of your platform
jasonthorsness
Seems like maybe it will be a challenge to use, but like a lot of template stuff the complexity gets buried in the library. The language gets a lot of ridicule but I like the no-compromise push to help programmers eliminate unnecessary runtime work in their programs.
112233
The corollary of that is that most new c++ features get designed with the explicit intent of getting burried in std library implementation (with coroutines being excellent example).
Then, developer is presented with a nice, easy to use library interface, that violently explodes all the obscure implementation details in their face on first error.
Modern C++ is made, I feel, primarily to make STL implementation less embarrassing.
izger
C++ became more and more cryptic. Make C++ code creation programmable (templating) by basic c++ syntax at compile time. Make this a part of the standard library.
mystified5016
Wow. That is... certainly some syntax. It reminds me of Perl.
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.
adrian17
As for syntax, note that as the author says:
> 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 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.
nine_k
The comparison with Perl is apt, to my mind. Both Perl and C++ were early to their space, experimenting with concepts that were weird at the time, trying many approaches we now find ugly, and serving as examples of both successes and failures for the much better languages that came after them. The difference is that Perl5 stopped evolving some time ago, while C++ continues the same tendency unabashed.
(Scala is another example of such a language.)
stathibus
Are you suggesting that reflection is a new fancy thing and c++ is paving the way?
nine_k
No; OOP, metaprogramming, exceptions, etc were sort of new in early 1990s in an efficiently compiled language, so C++ was paving its part of way, in an awkward manner, trying hard to make abstractions zero-cost. (I mean mainstream; Common Lisp had all these for a long time, and in an elegant way, but the cost is non-zero.)
Equally, Perl explicitly tried a number of syntactic ways to do something; some stuck as good ideas, some were demonstrated to be... less good. I think it was important to explore and show that a particular approach has serious downsides in practice, but it's not necessary to stick to in once better alternatives are available.
adrian17
> 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.
PessimalDecimal
> compile-time reflection
> use a C# program to generate C code
These seem at odds.
jcelerier
you call the C# program that generates the code as part of your build system
PessimalDecimal
I get it. That's code generation. But it's not compile time reflection.
MoonGhost
How about thin C++ wrapper? The language with the same features, but more human friendly. It should be easily directly translated to C++. Preferably both ways.
layer8
There are languages like D and Carbon that attempt this. But there are too many existing large C++ code bases to not continue evolving C++ itself as well.
MoonGhost
I didn't say replace, but complexity is the biggest barrier for newcomers. A lot of typing comparing to other languages. I use professionally since 90th, the more popular it gets the better. And complexity is the major problem, not memory management like some argue.
sirwhinesalot
That's what Herb Sutter's cppfront/cpp2 is meant to do. Same semantics, sane defaults, nicer syntax (IMO).
nine_k
C++'s semantics are also broken in interesting ways: UB, implicit conversions, arrays, exceptions, exceptions from destructors, etc. Papering over them won't help all that much. It's more efficient to just swallow the bullet and switch to Rust (or maybe Zig).
jandrewrogers
Many of these inadvisable semantics are fixable in practice, they are just a default that requires additional effort to minimize or eliminate.
That aside, it isn't that easy to switch languages because C++ is more expressive in a systems context in important ways. Porting existing modern systems C++ to e.g. Rust makes this pretty obvious. (I think porting to Zig would likely be a bit easier but I've never actually tried.)
theICEBeardk
Yeah no switching a group of developers over to some new language is not more efficient. It is a lot of work. And on top of that you will need to add time to either wrap your existing code base or rewrite it with developers who are new to the language they are using. You will have folks who like that and can do it, but you will also have a lot who would have a much harder time with that. Most corporations would legitimately ask what is switching the language gaining us and is it worth it. Now the conclusion might be that it is worth it, but it is equally possible that they decide against it. Never underestimate inertia.
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p29...
That's on brand for C++ in general. It works well, but it's ugly as sin while doing it.
It doesn't fix everything, yet it fixes more than those that don't use them at all.
I have been using such tools since mid-1990's when coding in either C or C++, and sadly it is still a discussion point regarding adoption.
C++ does not win any prizes for being the most aesthetically pleasing programming language but the new syntax around reflection is pretty ugly even by those standards.
I'm just using all of this on embedded platforms without issues - and can't wait to use reflection there either, it'll be ultra useful for ESP32 and Teensy projects we're working on
You shouldn't try to avoid all of the stdlib, but rather vet what you use from it. Furthermore, stuff like LTO or -ffunction-sections -fdata-sections (+ --gc-section when linking) gets rid of unused symbols, as long as your toolchain provider compiles the C stdlib and C++ stdlib with these
> coroutines
Almost everything is customizable by user (including class-scoped operator new), is it not?
> Where should embedded c++ projects go now that they are not welcome anymore?
C++ is still very much suitable for embedded, and while I disagree with most of your post I agree that: 1) there should be a linker flag to wrap all std:: exceptions instead of using -Wl,-wrap for each individual exception when needed and 2) that embedded feels like a second-class citizen sometimes ("deprecating volatile" (reverted), #embed missing C++23 etc)
The point I tried to make is that you cannot reasonably use modern features of C++ with your own standard library — the amount of undocumented internal compiler details you have to guess and match exactly is becoming unmanageable. And while -fno-rtti and -fno-exceptions still miraculously are supported by compilers (it is explicitly non-conforming C++ even in "freestanding" implementation, according to 16.4.2.5) and stop compiler from emitting references to std::, other features like allocators do not have an "off" switch.
The initializer_list is a good example. N4950 17.10 explicitly tells you there are multiple valid ways to implement it. If you guess incorrectly, your program will crash.
So current C++ standard library must be treated like part of the compiler implementation. It did not use to be like that.
There was the C standard library that could be consumed by C++ applications, the C++ frameworks specific to each compiler vendor that sadly are no longer a thing, and the C++ ARM de facto standard with only iostreams, being used as the initial discussion for what would become C++98, a decade later.
Many of C++ warts are related to this historical evolution, like how many variations of string, arrays and other collection classes do you want to use in a single project?
Yes, correct, what I meant is that you can still use many features like type_traits, bit_cast, initializer_list, span, array etc. Of course, std::string and friends are a bit no-no in very memory constrained cases.
> So current C++ standard library must be treated like part of the compiler implementation.
Indeed. Though even in C, compiler will assume C standard library is present unless you explicitly tell it not to (it optimizes calls to memcpy and will emit memset calls when initializing variables to 0)
C++23 is not abandoning embedded. Instead there is a tonne of misinformation around that is confusing people. You can easily tell what parts of the STD is available by looking up the concept of Freestanding, which is a legitimate part of the c++ standard which tells you what is absolutely safe to use in embedded. Usually some of the non-freestanding stuff is also safe to use.
Then what you do is add support for the ETL (github.com/ETLCPP/) which will help you by offering STD like classes for the parts that are not safe to use or just give you the std class wrapped in their namespace.
What we do is turn off RTTI and for now exceptions (most compilers let you do that with ease but we are looking at maybe using them in the future because of recent research indicating it would be possible and save us binary size at the same time) and you lean heavily into the constexpr side of things because anything you can get the compiler that is running nightly or on your PC to do rather than on the embedded system is just fine. We do not use coroutines so there I have no opinion.
Personally I am looking forward to the Reflection stuff because it is all compile time (and no that does not mean that your std on your pc somehow leaks illegal functions into your code constexpr/consteval is embedded safe) and it will allow me to make code that will be easier to debug than the recursive template expansions are now (stepping through a recursion is bad even if we strictly limit the depth of them but reflection will allow me to do an expansion into a flat set of ifs instead).
Yes, compilers support turning off exceptions and RTTI, for a long time now. C++ language does not. They could have supported it, but have chosen to explicitly not, as seen in the above section from the standard.
Nothing the committee does seem to support embedded. I will be glad to be shown I'm wrong.
Imagine Linux devs would start worrying about what "C standard" wants xD.
In my industry (gamedev), folks never needed to worry about how non-standard our code is either. As long as it ships on relevant half-dozen platforms and gets job done, its fine. One does not get a Boy scout badge for being standard compliant.
In fact they rely on lots of hardware UB for some console tricks.
Then, developer is presented with a nice, easy to use library interface, that violently explodes all the obscure implementation details in their face on first error.
Modern C++ is made, I feel, primarily to make STL implementation less embarrassing.
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.
(Scala is another example of such a language.)
Equally, Perl explicitly tried a number of syntactic ways to do something; some stuck as good ideas, some were demonstrated to be... less good. I think it was important to explore and show that a particular approach has serious downsides in practice, but it's not necessary to stick to in once better alternatives are available.
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.
> use a C# program to generate C code
These seem at odds.
That aside, it isn't that easy to switch languages because C++ is more expressive in a systems context in important ways. Porting existing modern systems C++ to e.g. Rust makes this pretty obvious. (I think porting to Zig would likely be a bit easier but I've never actually tried.)