18 ms·
Std::visit is everything wrong with modern C++ (2017)
- bsaul 6y agoMy field barely use C++, so i wonder : do people still have the feeling that "C++" is not going anywhere, or is it just C that's unavoidable ? I feel like except maybe in complex video-game engines, C++ is slowly but surely getting replaced by more adapted languages.
- verall 6y agoYes, I work in C++ systems, I feel huge job security. I don't see demand for C++ systems engineers going down in the next 10 years at all. The field evolves, it is just very slow.
- ncmncm 6y agoJudging by ballooning attendance at rapidly multiplying C++ conferences, the number of C++ programmers is growing faster than ever. More C++ programmers are newly hired, in any given week, than the total at work using Rust. That will be true for some time to come. Here on HN you can always get upvotes by promoting Rust (and it really is the only interesting new systems language in 30 years) or complaining about C++, but C++ is going nowhere but upwards. C++20 is better than '17, and C++23 will be better than '20. A language has achieved something when there are more articles complaining about it than about having succeeded in coding something using it.
- bloodorange 6y agoI love rust. Yet, I do not understand the hate for C++. I've done a lot of work using C++ and a lot of being able to use the language effectively comes from knowing why things are a certain way. Given its constraints, I think it is a brilliant language and I agree with your view of it. However, if someone were to ask my advice on which programming language s/he ought to learn, I usually point to python as its easy to begin and unless the goal is to get into game development, financial institutions or something like that, where C++ is the dominant language, I do not recommend it. It takes a lot of time before the complexity begins to disappear from one's mental model of the language. After that point though, one can see why it's a solid language.
- cranekam 6y agoVisit the last time this was discussed here: https://news.ycombinator.com/item?id=15249994 https://news.ycombinator.com/item?id=15249994
- rramadass 6y agoMuch better discussion with the proper answer provided. Everybody here should read this.
- nondave 6y agoThe post raises some good points, but I nonetheless think that C++ is a better language for having std::variant. It can result in some very clear, expressive code, despite the boilerplate and dabbling with more advanced language features that it necessitates. Indeed, from the article: "In spite of all of this, I’ll be busy encouraging my coworkers to use variant if anybody needs me. Sum types are such a useful concept that they’re worth the pain[...]"
- socialdemocrat 6y agoCode is not there to be written but to be read. I don’t see how this improves readability and hence I fail to see how it adds any benefit. Just another opportunity for C++ geeks to show off.
- runawaybottle 6y agoIt looks awful. Most languages should look like pseudo code, otherwise just accept defeat and admit you did not create an intuitive language (not everyone is good at making one). This is my biggest fear with arcane Typescript at the moment. Edit: I have seen Typescript that looks like this, but some of you could more easily corroborate this claim that are working with it more persistently.
- moron4hire 6y agoWith TypeScript, you have kind of an impossible problem: type check a program, but maintain compatibility with extant JavaScript libraries. There are JS idioms that don't match to TS very well without creating some pretty complex union types. And it's very easy to end up with ad hoc types all over everywhere, only to escape to using `any` when it becomes too much mental overhead. Previously, I had been very diligent about using JSDoc to annotate methods and fields with types, which gave me a lot of information with which to work in my IDE. But JSDoc is not a type system, it's very cumbersome to express ad hoc object structures, and being "very diligent" is not "perfectly diligent", so the TS porting process discovered some subtle bugs where I had, for example, guessed wrong on the type of something coming from a library (I also forked and ported most of my dependencies), or changed the return type of a method but missed a couple of very rare calls of it. Ad hoc union types in signatures gets super line-noisey. You can avoid it by diligently (there's that word again) using interfaces and named types. I think type expressions get overused in TS, out of some hope they can make the language into Haskell. That is not the case, and most of the time it's better to use interfaces. Still, I have a dependency that can only be included via a script tag. This wasn't too much of a burden in JS, because the language does basically nothing for you. But using it in my TS port took a lot of effort. I had to download the source of it, patch in a tsconfig, generate .d.ts files, and fix them up where they had escaped out to `any`. It's still not perfect. The quality of the library is pretty low to begin with, but it's the only way to interface with a WebRTC server I'm using. I'm pretty close to deciding to use a different WebRTC server (probably even one I write on my own), just to fix the remaining issues. That said, if you're starting from scratch, TS can be very clean and push you towards much better designs. It's hard to do the wrong thing. You can still do it, but it's hard. You need to have that ability, though, because there's just too much "wrong" with everyone else's code out there. I don't mind that, too much. The only thing I mind is that it's very difficult to distribute a library in such a way that it can be used with a TS project without a lot of warnings or errors, especially if you're using Webpack (ugh, Webpack generates some uuuugly bundles). So even though a lot of my dependencies offer TS-generated bundles or .d.ts files, it still doesn't work very well. You really need to be distributing TS source for the library, and not relying on any conditional compilation that your consumers will have to replicate in their own builds.
- nly 6y agoI actually prefer the overloads or constexpr branches to make_visitor because it's easier to see what's going on. Anyway, until C++ gets pattern matching for the simple cases make_visitor is the best we can do. If you want advanced variant use cases you can always use boost::variant (v1) which supports multivisitation, which coupled with templates is exceptionally powerful. If you want to go even further with open multimethods you can checkout the yomm2 library. As always C++ gives you options
- jsnell 6y agoThe constexpr version looks like it can't guarantee at compile time that all the variants are handled. At least to me, statically ensuring full coverage seems like an absolutely crucial part of effectively using sum types. Is there some kind of deeper magic going on in the "constexpr if" version than it looks like?
- nly 6y agoNo I think you're right, but this is fine. There may be instances where you don't want to handle every possible data type and other values should result in a no-op. If you want full coverage then visitation guarantee that statically. Multi-visitation over m variants each with N possible types also does this statically with N^m instantiations. This balloons quickly, which is why I mentioned yomm2, which can add these handlers dynamically at runtime for polymorphic types.
- bvaldivielso 6y agoI think it can guarantee full coverage. You just need an else branch with `static_assert(false)` or something along those lines
- jsnell 6y agoIf you need to put in the fallback "else" yourself, with no error or even warning if you forget to do so, it's not much of a guarantee :) (And thinking about this a bit more, there's presumably also the opposite case: if you add a branch for a type that's not a member of the variant / remove a type from a variant without updating all the visitors, it'd just happily compile with no indication that anything is wrong.)
- vasama 6y agoWe got here because adding library features is a lot easier and less risky than adding language features. Work on pattern matching [1] is ongoing, but the bar for entry for a language feature is much higher and as such takes a longer time. 1. http://wg21.link/p1371 http://wg21.link/p1371
- otabdeveloper4 6y agoI don't like this proposal, looks ugly and very poorly thought out. I'll keep std::visit for now, thanks.
- deleted 6y ago[deleted]
- socialdemocrat 6y agoI think using this feature is the wrong advice. Frankly I think much of modern C++ should be rejected. If you want to use a language like with those features then fricken switch language! There are countless better alternatives today: Rust, Swift, D, Haskell etc. Many are stuck on C++ for legacy reasons. I have worked in such large C++ legacy systems for many years in the past. The average programmer simply cannot deal effectively with this modern C++. It is too complex. I’ve been down that road being exited about new C++ features only to realize that I’ve actually reduced the productivity of my co-workers. Often I wonder if C++ programmers all suffer a Stockholm syndrome. They have come to sympathize with their hostage taker C++, making up excuses for the many ways C++ abuse and terrorize them. I go to these C++ conference talks in occasion and I see people in ecstatic praise about how some genius C++ guru came up with an elaborate convoluted solution to something which is like two lines of code in a sane programming language. Seriously I think C++ has drained so much of their brainpower that they have simply not been able to look outside and see how the grass is greener everywhere else.
- otabdeveloper4 6y ago> There are countless better alternatives today: Rust, Swift, D, Haskell etc. These aren't alternatives and are strictly worse as far as language power and expressiveness goes. There is some value in simpler and dumber languages, but your post essentially boils down to an anti-intellectual "programming is hard, let's go shopping instead". Some people need complex tools to solve complex problems.
- dralley 6y agoRust and D are not "strictly worse". They are better in some respects and occasionally worse in others, but even there the gap has been shrinking.
- otabdeveloper4 6y agoThe context makes it clear that what's meant is "strictly worse as a C++ alternative". Rust, like you said, isn't there yet. And I'd take issue with the claim that the gap has been shrinking - C++ right now is developing at a faster pace than Rust.
- GnarfGnarf 6y agoI've been writing C++ since 1990. I make a good living from a little C++ app (500KLOC) that sells in fifty countries. I love the ultra-static typing. It's saved my bacon many a times. Long ago "modern" C++ crossed into a territory that I will never understand or use. STL is the fanciest stuff I write. Code needs to be maintainable. If I have to hire geniuses to collaborate on my code, that's not going to be cost-effective. So I use a conservative subset, and thank my lucky stars I don't need to pay attention to the latest exotica.
- MrBuddyCasino 6y agoSince this is your business, your incentives are aligned: develop a product by means of a tool that needs to be productive and cost effective. In other words, you have skin in the game, unlike the average employed coder. This changes perspective.
- bsenftner 6y agoLikewise. I've been writing C professionally since '83, and thru the decades of my career spanning computer graphics, embedded systems, video games, film VFX, high compute web apps, and now facial recognition - the rule has been using a conservative subset of C++. STL being about as fancy as one gets, with an eye towards maintainability has been satisfying. By working with and employing people with backgrounds like former video game developers, they know the value of only optimizing bottlenecks. Clean easy to read C++ that any C programmer could read works, very well.
- dkersten 6y ago> Long ago "modern" C++ crossed into a territory that I will never understand or use. Do you know where the line is? C++11? Later versions? I ask because I quite like a lot of the changes that C++11 brought to the language, I feel they make my code much simpler to understand and maintain, but I got very confused by post-11 C++ for a long time to the point where I said I'd never bother with C++17 or later (14 turned out not to be too scary once I actually sat down with it) because 17 and 20 added so much stuff that just looked beyond me. But I've since changed my mind and have started to experiment with C++20, after I sat down and read a few articles and watched a few cppcon videos on the changes. I don't know all that these versions have to offer, but I do understand the main changes enough to get by and most of them do make the language better, in my opinion. Overall, the language is a mess, simply because they keep adding to it and very little stuff gets deprecated, removed, changed or cleaned. That leaves a large and messy language, especially compared to more recent deliberately-designed languages, but it turned out I could understand "most" of the things, once I had someone explain them to me. A few times ;) Having said that, it probably isn't worth your time to "modernise" the C++ in your money-making codebase at this stage, if you're comfortable with it how it is. You likely wouldn't see a return in the effort it would take.
- yudlejoza 6y agoC++ is a sh-t, bloated, clusterf*ck of a language. No need to get into nuance, and finesse, and hum and haw. Life is moving fast, and super-busy. There are serious and interesting problems in CS that need to tackled, and need to be tackled with full focus. Mastering C++, so you could implement an iota of a feature in a couple days that would take five minutes for a python or js programmer, is not one of them.
- carlhjerpe 6y agoEvery task isn't a task that can be solved with 5 minutes of Python or JS. There are task that are straight up unsuitable for a language like Python or JS.
- deleted 6y ago[deleted]
- yudlejoza 6y agoyou mean the tasks that can be done roughly 10x to 1000x better than with C++ using some combination of the following: - C, assembly - MPI, OpenMP - GPGPU ?
- AnimalMuppet 6y agoThere are exactly zero tasks that can be done 10x better with C than with C++.
- f00zz 6y agoI love how there's a derisive comment saying "the author doesn't even understand SFINAE".
- okareaman 6y agoI've written a lot of C++ code but I'm retired so it's been a while. After reading about SFINAE I had a strong feeling that C++ reminds me of the US Tax code or fits the definition of the word Byzantine. https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error https://en.wikipedia.org/wiki/Substitution_failure_is_not_an...
- f00zz 6y agoI need to look up how to use std::enable_if every time. Thankfully C++20 concepts made it obsolete.
- GnarfGnarf 6y agoC++ is like the 1947 Boeing 377 Stratocruiser: its 28-cylinder radial engine was the pinnacle of piston technology, it was impossible to tune, and shook so badly it literally ripped from the wings.
- okareaman 6y agoYoung people wont' get this but see also Rube Goldberg Contraption https://en.wikipedia.org/wiki/Rube_Goldberg_machine https://en.wikipedia.org/wiki/Rube_Goldberg_machine
- mlang23 6y agoThere is a point, but it is overstretched. make_overloads is actually a cool trick. Yes, it would be nice if there was a version of this in the standard library, but then again, it is just a few lines of magic. That will not be the first lines of magic in your C++ codebase. If you compare the sum type and pattern matching story with modern languages, sure, you will find C++ is lacking. And it is. However, such a comparison is ignoring history. I actually like variant. It is the best sum type we have in C++.
- usrnm 6y agoOr you can simply use holds_alternative with a simple if expression. A little bit less efficient, but much more readable
- dbattaglia 6y agoI've also found std::get_if to be halfway decent in terms of readability. You end up with a if/else-if block that looks a bit like an ugly verbose pattern match: if (const auto sPtr (std::get_if<std::string>(&myVariant)); sPtr) { /* use sPtr */ } else if (const auto iPtr (std::get_if<int>(&myVariant)); iPtr) { /* use iPtr */ }
- quietbritishjim 6y agoAgreed that std::get_if is nice, it avoids redundantly checking then fetching the value compared to using holds_alternative. But there's no need to have that separate clause in the if statement to test the truthiness of pointer, that's already what will happen from the initialisation: if (std::string* s = std::get_if<std::string>(&myVariant)) { // Use s } else if (int* i = std::get_if<std::string>(&myVariant)) { // Use i } else { throw std::logic_error("Unexpected type in variant"); } The hardcore modern C++ gang will vigorously complain that using this kind of chained if will - horrors! - yield a runtime error instead of a compile time error if you have forgotten one of the types that the variant can hold. I have some sympathy for that point of view, but in practice the small likelihood of that bug happening and the small extra difficulty of finding it at runtime is completely overshadowed by the extra complexity of std::visit. I don't agree with GP's claim that chained ifs are likely to be slower than std::visit. If anything, it seems like they'd give the compiler less work to do to optimise well than the layers of funtion calls involved in std::visit, especially since std::visit is ultimately going to expand to chained if statements anyway. (A digression that's maybe dangerous to bring up: You've made an odd choice out of the many initialisation syntaxes to use. Many in the hardcore modern C++ gang would advise always using auto and brace initialisation, with no equals, even for really simple variable definitions e.g. auto i{int(3)};. Personally I find that obtuse and would write that as int i = 3, at least for simple things (raw pointers like in the example above fall into that category for me). Direct initialisation with parentheses isn't normally advocated by either old-timers or modern C++ fans, in part because of the most vexing parse - did you mean to use braces?)
- deleted 6y ago[deleted]
- the_gipsy 6y agoOn a tangent, how do you do sum types in java? I recently wanted to use them and all I could figure out was some subclasses and `instanceof` as poor man's pattern matching.
- edflsafoiewq 6y agoThat's how. Sum types were largely dropped from imperative languages in favor of subclassing during the OO craze (which is why they had to be re-discovered from the functional world).
- nchi3 6y agoLook up sealed classes if you're using Java 15. There's no proper exhaustive pattern matching for them yet though, but that's coming in a future version.
- vlovich123 6y agoBeyond the ergonomics, `std::variant` is horribly outperformed by basic type erasure mechanisms or inheritance. Every time I've tried to use `std::variant` I've been unhappy with the runtime performance vs any other way to solve this problem (the compile time performance is expectedly atrocious compared to any other method). So you've got poor ergonomics, poor compilation performance, bloated code size, and poor runtime performance. I've written an enormous amount of C++ code from applications to libraries to system development. I literally have never found a place where `std::variant` has any positives that can't be written a different, clearer way with drastically better compile time and run time performance.
- jpcooper 6y agoI don’t have a huge amount of experience writing C++ code. What are these other more efficient ways? I suppose virtual classes is one, but it becomes difficult to add functions.
- jcelerier 6y ago> Beyond the ergonomics, `std::variant` is horribly outperformed by basic type erasure mechanisms or inheritance. uh, I have yet to see variants beaten by inheritance in practice - in particular as soon as you have type erasure all your containers have an additional level of indirection (which can be solved partially by Boost.PolyCollection but that adds its own layer of complexity) and that has been a much bigger perf-killer - with variants memory is nicely packed when iterating over your array of values and that's by far the biggest performance thing you can do. You also need to allocate memory dynamically everywhere which is a no-go in a lot of cases.
- vlovich123 6y agoLike I said, I was shocked but I was building something recently and, like you, reached for std::variant first to make the solution more generic while, in theory, outperforming type erasure/inheritance. Thankfully I had thorough micro benchmarks and was able to demonstrate convincingly that type erasure was drastically faster in practice, despite the theory (and virtual inheritance was better than both).. I think what ends up happening is that std::variant and how you access it is pretty code bloaty and hasn’t been optimized yet properly by library authors and compilers. It’s also not particularly ergonomic so even though it’s been around for 3 years there’s not really any incentive (it’s slow and unergonomic, slow adoption, not the highest area for focusing on improving ergonomics and performance). I think type erasure is overly criticized on how bad it performs. A single indirection in a hot loop is actually extremely CPU friendly as it’s predictor will keep the pipeline filled and you’ll be unlikely to end up flushing the pipe. It’s something the CPU has been optimized for since polymorphism is present in one way or another in all paradigms. Equally I suspect vtable costs are denigrated for no good reason. Like the cost is there (and you can accidentally overuse it) but the vtable is information to the compiler (+ it wouldn’t surprise me if CPUs had special tricks to make c++ vtables even faster) that can result in faster perf than you could achieve any other way. Again. I’m not talking about the theory here. I’m talking about the actual performance I wrote thorough microbenchmarks on. The amount of code that gets generated to do discriminated std::variant unions is absurd which I suspect is a large part of the problem. I’m impressed that the standards body managed to pull off being so bad on so many dimensions. I’m not saying it’s a bad library. It has very specific requirements it’s trying to maintain specific to how the STL is written. Abseil’s HashTable reimplementation (SwissTable) should show how many of the invariants the STL likes to enforce in practice aren’t invariants anyone cares about and inhibit good performance. Said another way. std::variant is probably the most efficient discriminated union you could get past the standards body to be included in the STL. Rust’s performs way better on any metric and that should give you a glimpse that the incentives in the standards body are misaligned with what’s actually needed to keep C++ healthy. I think - I haven’t benchmarked but tagged unions are so embedded in the language and type system rather than as “dumb” library code I can’t imagine they have the inefficiency baggage of std::variant.
- danybittel 6y agoI usually just use switch(std::variant:index()) and then std::get<int>(). Which worked reasonably well. But then I stopped using std::variant altogether, because in the end it never gave enough benefits. These days I use reinterpret_cast and cast away all my problems.
- spion 6y agoIts probably time to give C++ a rest and stop evolving it. We've probably long since passed the point where any evolution gives a net benefit to engineers, and its time to let other languages take over.
- kzrdude 6y agoGiven all this, use a dumb old macro to make a simpler interface with a macro that expands to type safe code. That will save a lot of unnecessary syntactical noise and overwinter you until better pattern matching support comes along.
- w4rh4wk5 6y agoIf anyone is interested, we had a small utility in one of our C++ code-bases to leverage ADTs where it made sense. Here is the implementation. https://github.com/insieme/insieme/blob/master/code/utils/include/insieme/utils/adt.h https://github.com/insieme/insieme/blob/master/code/utils/in... Some tests illustrating the usage can be found here: https://github.com/insieme/insieme/blob/master/code/utils/test/adt.cc https://github.com/insieme/insieme/blob/master/code/utils/te...
- amluto 6y agoAs a long-time user of C++ variants with a home-grown tool much like the article’s make_visitor, I think there’s a much bigger problem with C++ variants: the sub-types don’t have names. If you genuinely want your variant to just be a mildly polymorphic object, this is okay. But you probably don’t. Imagine the classic use of variants for a parse tree: variant<string, shared_ptr<Node>> Okay, presumably the string is a token and the Node is a subtree. This is already a bit unpleasant, but at least it’s serviceable. But then you discover that you really want tokens and symbol names to be separate. You try: variant<string, string, shared_ptr<Node>> And you lose, because the two unnamed string cases are ambiguous. To work around this, you need to wrap the strings in newtypes, which is quite verbose in C++. And you start to wonder why, after all these years, C++ hasn’t sucked it up and come up with real sum types in the language. (For those who are too stuck in C++ land, this problem simply does not exist in a language like Rust.)
- CodesInChaos 6y agoUnions discriminated by type could be useful in Rust, especially if you could use them without declaring the union type explicitly. It'd enable something like `Result<..., IoError | ParseError>` without having to manually create a union type, implement the appropriate traits for it, etc. Unfortunately it opens a can of worms related to type identity, so I don't expect to see such a feature soon.
- steveklabnik 6y agoThere are a group of people pushing for it ("anonymous enums") but it is unclear if it will ever happen or not, yes.
- quietbritishjim 6y ago> To work around this, you need to wrap the strings in newtypes That is certainly an option, but actually std::variant<string, string, shared_ptr<Node>> does still work for most operations (std::visit is not one of them though). So another option is to use that typelist directly and switch to index-based access, at least for those entries: if (std::string* s = std::get_if<0>(myVariant)) { // Use s } else if (std::string* s = std::get_if<1>(myVariant)) { // Use s } else if (NodePtr* node = std::get_if<2>(myVariant)) { // Use node } else { throw std::logic_error("Unknown variant entry"); } At that point you'll probably want to define an enum (an old-school one so it implicitly converts to int) to identify the cases symbollically, which admittedly is pretty messy given that it's up to you, the maintainer, to keep the enum in sync with the actual type list in the variant. But it's hardly the worst coding sin ever, and better than any alternative I can think of (short of switching to another language). As you can see from my comment further down, I think a chain of if statements is cleaner than std::visit anyway, precisely because of the complexity problems of std::visit discussed in the article.
- sesuximo 6y agoAs the article says: - you can write 10LOC to have a lambda visitor. Idk why the standard doesn’t do this for you but it’s not a big deal - sum types are super useful and your language will want some sort of niceties/extra characters to deal with them (and all options will be better than unions which are an unsafe mess) So ... what’s the big issue? C++ is complicated? It’s a language designed to give the programmer full control when they want it. You don’t have to constantly use every bit of it.
- mhh__ 6y agoThe problem with C++ is really the standards committee overcomplicating things. They've done some important work, like the memory model, but they don't have the confidence to make the decision quickly enough. Compare template constraints in D and C++. C++ ends up with concepts after something like 25 years in the making, and they add huge amounts of syntax and bloat, whereas in D it's just an if statement after before the template body. The way static if was considered was bizarre too
- busrf 6y agoI actually think the last version, using a single lambda with constexpr ifs inside, looks completely reasonable. But yeah the first time I had to use boost::variant (which is what the version in the standard library is based on) I thought the visitor was too much boilerplate. Things have gotten better since then, but many people are not even on C++17 yet ):
- tayistay 6y agoIn many cases you can get away with just using a struct with a tag (no unions). Yes, it wastes memory, and isn't as safe as a proper sum type, but at least it's memory-safe for C++ objects and you can use a normal switch statement. Compiler warns if your switch isn't exhaustive. Compile times are fast too.
- jcelerier 6y ago> In many cases you can get away with just using a struct with a tag (no unions). Yes, it wastes memory, so... it's a no-go ? if you don't care about wasting memory just use python or JS
- tayistay 6y agoObviously, there are various reasons why you can't "just use python or JS" for a particular project.
- vrtx0 6y agoI don’t think any of the approaches in this article are appropriate for the stated problem. Specifically, parsing data from an INI/JSON/text file (or TLV-like encoding, predefined format/protocol, etc.), the parser can produce appropriate structures for the underlying data. This is the phase when you analyze (and likely validate) the input data, so you’d know the appropriate type after parsing. You may want your parse tree to produce objects instead of fundamental C++ types (e.g. with functions like isNull(), isValid(), etc.). A common approach might be to produce a simple AST where each node includes just the fundamental type you’re interested in. Then you can simply follow the visitor pattern and write visitors for things like serializing/deserializing, generating/traversing/executing trees/graphs/code, etc... Perhaps I’m missing something though? A more concrete definition of the problem would be helpful...
- Rangi42 6y agoWith C++17's compile-time conditionals, plus good old C preprocessor macros, you could do this: #define MATCH_VARIANT(x) [](auto& x) #define CASE_VARIANT(x, T) constexpr (std::is_same_v<std::decay_t<decltype(x)>, T>) MATCH_VARIANT(arg) { if CASE_VARIANT(arg, string) { printf("string: %s\n", arg.c_str()); // ... } else if CASE_VARIANT(arg, int) { printf("integer: %d\n", arg); // ... } else if CASE_VARIANT(arg, bool) { printf("bool: %d\n", arg); // ... } } (MATCH_VARIANT could be extended to allow capturing vairables, or just omit that macro, it saves less typing than CASE_VARIANT anyway.)
- erhfu987dsghvjk 6y agoan absurd amount of work and prerequisite knowledge for a simple feature. Really the flaw of C++