What a neat concept. It never ceases to amaze me what people can arm-bar C++ into doing through typesystem, metaprogramming, and operator overloading shenanigans.
I hope I never see this in a production codebase, though!
On a kind of related note, I want to see an alternate history where C++ had support for Rust-ish proc macros, or Racket-ish macros. What would that language even look like? There'd probably be no need to hack existing C++ language semantics to add new features, you'd "just" generate the code that the meta-language lowers to.
I say that because I would hate to be the person that has to ramp up a junior engineer on a project that uses a library like this.
I've written my fair share of code like this, and while you and I might grok it (and probably have a fun time figuring out how it works in the process), most people will hit a brick wall the moment they have to debug the 1000 line error that Clang or GCC will give you when there's a type error.
This is the kind of thing that belongs in language-feature land (so you get tooling support, reasonable compiler errors, etc), not library-land.
> This is the kind of thing that belongs in language-feature land (so you get tooling support, reasonable compiler errors, etc), not library-land.
Again bringing up my STL example: This is just not how C++ runs. I’ve seen my fair share of std::__v1::basic_string<char, char_traits<char>, DefaultAllocator<>> errors. Some would argue a string type should be language-level, and they might be right, but the committee disagrees.
Right, well, I think we can agree the committee makes some weird calls sometimes with respect to language design. We only got concepts and coroutines (relatively) recently, and they're still kind of warty. `std::range` is beautiful and I'd still only use it sparingly. The C++ language, in my experience, is a language best served as a safe subset with "magic" and advanced features eschewed as much as possible.
The C++ STL has a lot of templated code in it (obviously), but at least the amount of weird tricks, such as template recursion, is fairly small (ignoring newer additions like `std::range`). And even then, compiler errors can make an experienced engineer's eyes water. At least you can paste most errors into Google and find a relevant StackOverflow post about how to fix it.
Involving a library like this, though - best of luck, the engineer is on their own.
I wouldn't even say "kind of warty." I think that Titus Winters was spot on a few years ago that he predicted that concepts are unrefactorable in sufficiently large codebases.
It's my impression that people tend to underestimate "junior engineers" in the C++ world. I'm always impressed at conferences that there are so many young people holding presentations about really advanced topics.
IMHO this "no junior will ever understand that" attitude comes mostly from older folks who learned C++ as C with classes and to whom even the STL is a work of the devil.
Those people giving talks at conferences are the best of the best. 99.99% of people who write C++ have no interest in the arcane depths of the language, and would rather things "just work" over having a library that does something cool, but hard to debug when things go pear shaped.
Not all, the people giving talks are just people who happen to be interested and curious.
This idea that only the best of the best are able to be curious and learn the nature of the tools they use at a very deep level is misguided and furthermore promotes a kind of anti-intellectualism.
It's also a large part of why I think many developers get burned out, imagine working in a culture with other engineers where people are kind of pressured to only do boring work, stick to boring features, only write code in a very narrow manner that satisfies the lowest common denominator using the same old boilerplate over and over again and keep doing that for a decade or longer versus an engineering culture that values people being inquisitive, breaking out of their comfort zone, writing interesting libraries that abstract out common patterns and eliminates painful repetition, and who appreciate the inherent complexity of challenging problems instead of treating it like some arcane voodoo that only the select few can understand.
It's interesting you mention junior engineers. My experience is the complete opposite, it's people who have been programming for many years who hate learning stuff like this and want to stick to their comfortable way of programming, whereas those who have programmed for fewer years enjoy learning about this and actually have fun doing so.
Perhaps unsurprisingly the author of this library looks to have only a few years of experience themselves.
It may be fun, yes, but I've found that experienced engineers aren't eschewing libraries like this because they hate fun and learning new things.
Rather, it's because experience has taught them that the looks-cool slightly-magical stuff like this is hard to maintain once a codebase reaches a certain size, and maintainable code is boring and obvious.
In my experience at G introducing modern C++, the most senior engineers had the biggest problems with it. Junior engineers hated the cryptic errors a lot more, though.
I still remember (well, have screenshotted) this amazing error I encountered in my first exposure to C++. I was writing code for an assignment implementing my own vector container from scratch, and got "no suitable user-defined conversion from "CSXXX::vector::r_iterator" to "CSXXX::vector::r_iterator" exists".
Thank you GCC for telling me that I'm missing a copy/move constructor (can't remember which it was). Clear as mud as always. At least with the several pages of template instantiation errors you can scroll to the top where useful information can sometimes be found.
I'm mixed on this. On the one hand, I agree with you. Well isolated systems can be messy internally without causing trouble. On the other hand, error messages. STL has famously awful error messages. I do want to know what the compiler will spit out when I've got a syntax error or a type error when using one of these constructions. Because teaching people new to C++ what "you stuck a non-movable type in std::vector" error messages look like is not a trivial task.
> Do you also hope to never see the STL in production? Because internally, that thing is high level unreadable C++.
SGI/HP STL isn't nearly as bad as modern implementations, though they all __share _Weird ____identifier names, mostly because of C/C++ identifier rules (_[A-Z] and anything with __ are reserved for the implementation, everything else might just be #define'd to rick-roll by whatever program is including you).
Interesting - in the godbolt.org link, looks like both GCC and Clang optimize the code to run at compile time, so the resulting function is just "mov eax, 24".
But MSVC latest instead generates the full runtime code and function call.
I wonder if it hit some max execution time heuristic or something like that.
As a quick test, I added a static_assert() that depends on the returned value, which seems to force complete evaluation to happen at compile time. With that, all 3 compilers generate the expected single-instruction implementation: https://godbolt.org/z/onMannzM5
Sadly lvariant did not get into C++ language. The library supports pattern matching against std::variant/std::any and class inheritance as a replacement.
There has been a proposal to add pattern matching to C++ for nearly a decade now https://www.stroustrup.com/OpenPatternMatching.pdf Shame it never got in. With language support for matching, sum types and product types it would be a completely different language.
Although the phrase "pattern matching" is involved these are completely different features.
Also, unlike for regular expressions what you actually want here is typically a language feature, even if a bunch of the lifting is done in your standard library. In particular you probably want a keyword (or in several C++ pattern matching proposals, more than one) and new behaviour not just a few functions and constants.
You could think of this as a bit like "switch" but on the other hand if your insight into switch is that it's basically a computed go-to wearing fancy dress then no, not that, the thing ordinary people use switch for.
Actually this library (match(it)) is more similar to Racket. You can see there are lots of patterns borrowed from Racket pattern matching, say app pattern and ooo pattern.
Thought it's about regex matching for switch-case, turns out it's very different, interesting though, is this an idea borrowed from Rust? Not sure if I'm going to use it or not.
Pattern matching has quite a long history[1]; it's been around in some form since 1957. It's been enjoying a bit of a renaissance lately; I suspect that's a result of Haskell's popularity but I don't really know.
I hope I never see this in a production codebase, though!
On a kind of related note, I want to see an alternate history where C++ had support for Rust-ish proc macros, or Racket-ish macros. What would that language even look like? There'd probably be no need to hack existing C++ language semantics to add new features, you'd "just" generate the code that the meta-language lowers to.
Why? Looks completely readable, easy to understand to me.
Do you also hope to never see the STL in production? Because internally, that thing is high level unreadable C++.
I've written my fair share of code like this, and while you and I might grok it (and probably have a fun time figuring out how it works in the process), most people will hit a brick wall the moment they have to debug the 1000 line error that Clang or GCC will give you when there's a type error.
This is the kind of thing that belongs in language-feature land (so you get tooling support, reasonable compiler errors, etc), not library-land.
Again bringing up my STL example: This is just not how C++ runs. I’ve seen my fair share of std::__v1::basic_string<char, char_traits<char>, DefaultAllocator<>> errors. Some would argue a string type should be language-level, and they might be right, but the committee disagrees.
The C++ STL has a lot of templated code in it (obviously), but at least the amount of weird tricks, such as template recursion, is fairly small (ignoring newer additions like `std::range`). And even then, compiler errors can make an experienced engineer's eyes water. At least you can paste most errors into Google and find a relevant StackOverflow post about how to fix it.
Involving a library like this, though - best of luck, the engineer is on their own.
IMHO this "no junior will ever understand that" attitude comes mostly from older folks who learned C++ as C with classes and to whom even the STL is a work of the devil.
This idea that only the best of the best are able to be curious and learn the nature of the tools they use at a very deep level is misguided and furthermore promotes a kind of anti-intellectualism.
It's also a large part of why I think many developers get burned out, imagine working in a culture with other engineers where people are kind of pressured to only do boring work, stick to boring features, only write code in a very narrow manner that satisfies the lowest common denominator using the same old boilerplate over and over again and keep doing that for a decade or longer versus an engineering culture that values people being inquisitive, breaking out of their comfort zone, writing interesting libraries that abstract out common patterns and eliminates painful repetition, and who appreciate the inherent complexity of challenging problems instead of treating it like some arcane voodoo that only the select few can understand.
It makes me so damn glad I run my own company.
Perhaps unsurprisingly the author of this library looks to have only a few years of experience themselves.
Rather, it's because experience has taught them that the looks-cool slightly-magical stuff like this is hard to maintain once a codebase reaches a certain size, and maintainable code is boring and obvious.
Thank you GCC for telling me that I'm missing a copy/move constructor (can't remember which it was). Clear as mud as always. At least with the several pages of template instantiation errors you can scroll to the top where useful information can sometimes be found.
SGI/HP STL isn't nearly as bad as modern implementations, though they all __share _Weird ____identifier names, mostly because of C/C++ identifier rules (_[A-Z] and anything with __ are reserved for the implementation, everything else might just be #define'd to rick-roll by whatever program is including you).
But MSVC latest instead generates the full runtime code and function call.
Should this be reported anywhere?
As a quick test, I added a static_assert() that depends on the returned value, which seems to force complete evaluation to happen at compile time. With that, all 3 compilers generate the expected single-instruction implementation: https://godbolt.org/z/onMannzM5
Although if the C++ committee manages to add this without deprecating other things, I would be impressed.
Also, unlike for regular expressions what you actually want here is typically a language feature, even if a bunch of the lifting is done in your standard library. In particular you probably want a keyword (or in several C++ pattern matching proposals, more than one) and new behaviour not just a few functions and constants.
You could think of this as a bit like "switch" but on the other hand if your insight into switch is that it's basically a computed go-to wearing fancy dress then no, not that, the thing ordinary people use switch for.
[1] https://en.wikipedia.org/wiki/Pattern_matching
const fn factorial(n: u128) -> u128 { match n { 0 => 1, _ => n * factorial(n-1) } }
fn main() { dbg!(factorial(20)); }
Rust does not support view pattern (called in Haskell) or app pattern (called in Racket). And that has been implemented in this library.