I was very curious to see what C++ 26 brings to the table, since I haven't used C++ in a while.
When I saw the 'no boilerplate' example, the very first thought that came to my mind:
This is the ugliest, most cryptic and confusing piece of code I've ever seen.
Calling this 'no boilerplate' is an insult to the word 'boilerplate'.
Yeah, I can parse it for a minute or two and I mostly get it.
But if given the choice, I'd choose the C-macro implementation (which is 30+ years old) over this, every time. Or the good old switch case where I understand what's going on.
I understand that reflection is a powerful capability for C++, but the template-meta-cryptic-insanity is just too much to invite me back to this version of the language.
As a developer who doesn't really write C++ code I'm inclined to agree, but I think Herb Sutter's "syntax 2" project might provide a nice way out of that mess eventually.
I played around with cppfront over Christmas and it was a lot more ergonomic than my distant memories of C++11, which I don't even have negative memories of per se.
It is no different from any other language that compiles via C or C++ code generation, it got sold a bit differently due to his former position at WG21.
Well, if you mean "as an official C++ syntax" then I agree, and I suspect Sutter would agree as well. He labeled one talk about it a "Towards a Typescript for C++", after all[0].
But I do think it is different than other "compile to C++" languages, because it seems to be more of a personal case study for Sutter to figure out various reflection and metaprogramming features, and then "backport" those worked out ideas to regular C++ via proposals. And the latter don't have to match the CPP2 syntax at all.
In multiple examples he's given in talks the resulting "regular" C++ code is easier to read, mainly because the metaprogramming deals with so much boilerplate.
What Herb Stutter misses on his Typescript and Kotlin for C++ metaphor is the actual reality how those languages integrate, unlike cpp2.
Typescript is a linter, nothing else, type annotations for JavaScript. The two features that aren't present in JavaScript, enums and namespaces, are considered design mistakes and the team vouched to focus only on being a linter,and polyfill for older runtimes, when possible (some JS features require runtime support).
While Kotlin spews JVM bytecode many language constructs, like co-routines, make it one way, it is easy to call Java from Kotlin, the other way around requires boilerplate code, manipulating the additional classes generated by the Kotlin compiler for its semantics.
My point was that TypeScript isn't exactly about to replace JavaScript, which was what you were arguing. I'm honestly not sure what you're trying to argue now.
Like, yeah, what you say about TS and Kotlin is true about TS and Kotlin. But since you're not explaining what cpp2 does or plans to do differently, and why it matters, I'm not sure where you're going with that. It's probably obvious but I'm not getting it.
The metaphor Sutter was going for, as I see it, is that TS and Kotlin both added missing features to their host language. Most importantly reflection and decorators in TS, which are now becoming a standard in JS as well[0]. cpp2 mainly focuses on experimenting with reflection and metaprogramming as well, adding features currently missing in C++ by being a compiles-to-C++ language. Sutter has written C++ proposals what would allow give C++ similar reflection and metaprogramming capabilities based on what he discovered by working on cpp2. That's pretty comparable if you ask me.
> But if given the choice, I'd choose the C-macro implementation (which is 30+ years old) over this, every time.
Why? The implementation is not pretty, but you only need to write it once and then it works for all enums. The actual usage is trivial, it's just a function call.
The C macro version is horrendous in comparison. Why would I want to declare my enums like that just because I might want to print them?
Just wait for C++32 :-D. After all, we only got `std::string::starts_with` in C++20 and C++23 finally gave us `std::string::contains`. It's a clown show, you just need to take it with humor.
It is "cryptic" and "ugly" to you just because you're not familiar with it. You'd pick the macro-based implementation because you are familiar with it.
Seeing this argumentation is so tiresome, because it feels like there is a lack of self-awareness regarding what is "familiar" and what isn't, which is subconsciously translated to "ugly" and "bad".
Have you ever used other (modern) programming languages ?
In a lot of languages, you achieve the same with 1 line of code. It's not about familiarity, it's about the fact that it's a long and convoluted incantation to get the name of an enum.
Why do I have to be familiar with all those weird symbols just to do a trivial thing ?
And what if you want to implement something like Rust's "derive"? That is what the article shows.
As far as I understand you would have to mess with individual parser tokens in Rust instead of high-level structures like "enum" (C++ reflection). It would be much, much uglier to implement anything like "to_enum_string" in Rust as you would have to re-implement parts of the compiler to get the "enum" concept out of a list of tokens.
No, it is objectively cryptic and ugly. I honestly don’t understand how can anyone keep up with this garbage, but the ship has sailed long time ago. It is just a soup of symbols at this point.
Is it? I'm mostly used to (pre-)C++11 and the only unusual operators I see are ^^T (which I presume accesses the metadata info of T) and [:e:] (which I assume somehow casts the enumerator metadata 'e' to a constant value of T).
And template for but I assume that's like inline for like in zig.
requires is also new (not sure exactly when that appeared, it's after the last time I wrote C++ in anger) although I think it's fairly clear what it means. I can only guess at the other two.
Not familiar with Zig but AFAICT `inline for` is about instructing the compiler to unroll the loop, whereas `template for` means it can be evaluated at compile time and each loop iteration can have a different type for the iteration variable. It's a bit crazy but necessary for reflection to work usefully in the way the language sets it up.
Well yes, but the _effect_ is to unroll the loop for runtime, if the inline-for survives that long.
A for loop executed during comptime is just
const stuff = comptime stuff: {
for (0...8) |i| {
// etc, build up some stuff
}
break :stuff some_stuff;
};
The difference is that a comptime block won't leave behind runnable 'residue', only whatever data is constructed for later. An inline for might not leave behind an unrolled loop either, but it can.
I wish I understood the reason for the `std::define_static_array`... Why can't `std::meta::enumerators_of` just return something that can be iterated through????
It is kind of weird at first but the reason is that `std::vector` requires heap allocation and transient allocations are not allowed in `constexpr` contexts. The purpose of `std::define_static_array` is to promote the storage of the vector to static storage to eliminate the transient allocation issue, and so that the `template for` can work properly with it.
Is there a reason why `std::meta::enumerators_of`, a reflection feature that's surely almost exclusively going to be used in constexpr contexts, returns a value which doesn't work in constexpr contexts?
Just another example where C++ language features are incompatible with each other, to be fixed "in a later version" which may or may not happen. There are so many of those in C++. I desperately wish they'd just do it properly initially.
When I saw the 'no boilerplate' example, the very first thought that came to my mind:
This is the ugliest, most cryptic and confusing piece of code I've ever seen. Calling this 'no boilerplate' is an insult to the word 'boilerplate'.
Yeah, I can parse it for a minute or two and I mostly get it.
But if given the choice, I'd choose the C-macro implementation (which is 30+ years old) over this, every time. Or the good old switch case where I understand what's going on.
I understand that reflection is a powerful capability for C++, but the template-meta-cryptic-insanity is just too much to invite me back to this version of the language.