C++ Design Patterns - The Most Common Misconceptions (2 of N) - Klaus Iglberger - CppCon 2024
ฝัง
- เผยแพร่เมื่อ 5 ก.พ. 2025
- cppcon.org
---
C++ Design Patterns - The Most Common Misconceptions (2 of N) - Klaus Iglberger - CppCon 2024
---
Design patterns are everywhere, as they are the key to managing dependencies between software entities. But despite their fundamental importance, there are some common misconceptions about them, in particular about several of the most often used design patterns:
The modern form of the Visitor design pattern, std::variant, is often considered a replacement of virtual functions
The Decorator design pattern is sometimes mistaken as the Adapter design pattern
The Chain of Responsibility design pattern is often confused with Decorators
Explicit object parameters (“deducing this”) is often considered as a modern, better form of CRTP
In this talk I'll shed some light on these misconceptions and explain how to properly distinguish between the different design patterns.
---
Slides: github.com/Cpp...
Sponsored by JetBrains: www.jetbrains....
---
Klaus Iglberger
Klaus Iglberger is a freelancing C++ trainer and consultant and is currently on the payroll of Siemens in Nuremberg, Germany. He has finished his PhD in computer science in 2010 and since then is focused on large-scale C++ software design. He shares his experience in popular advanced C++ courses around the world (mainly in Germany, but also the EU and US). Additionally, he is the initiator and lead designer of the Blaze C++ math library (bitbucket.org/...) and the organizer of the Munich C++ user group (www.meetup.com....
---
CppCon is the annual, week-long face-to-face gathering for the entire C++ community. The conference is organized by the C++ community for the community. You will enjoy inspirational talks and a friendly atmosphere designed to help attendees learn from each other, meet interesting people, and generally have a stimulating experience. Taking place this year in Aurora, Colorado, near the Denver airport, and including multiple diverse tracks, the conference will appeal to anyone from C++ novices to experts.
Annual CppCon Conference - www.cppcon.org
/ cppcon
x.com/cppcon
/ cppconference
/ cppcon
mastodon.socia...
---
Videos Filmed & Edited by Bash Films: www.BashFilms.com
TH-cam Channel Managed by Digital Medium Ltd: events.digital...
---
#cpp #cplusplus #cppcon #cppprogramming #cplusplusprogramming #softwaredevelopment #softwareengineering #coding #code #computerscience #technology #technews #programming #programmer
Great talk as always by Klaus. His talks are always engaging and informational
So one thing I'll say about std::variant vs virtual functions/inheritance is that yes, once in a blue moon, when the planets align on Friday the 13th and the mythopoetic omens demand it, you'll actually have a real situation where you need open type extension. But it's about that rare. If you don't have a really clear articulation up front for why you're going to imminently need it, YAGNI. And this is probably the fundamental grift of the original OOP evangelism movement, convincing a lot of inexperienced developers to assume by default that they needed open type extension in every situation. Again, I'm not saying never, but there are performance and readability reasons why it should not be your first resort. Make that decision after a sober consideration of the tradeoffs, and if possible, architect your code so that kind of dynamism happens higher in your call stack, not in hot loops. And I don't think I'm fundamentally in disagreement with Klaus here, either, based on the final comparison table and his remarks afterwards about knowing your problem.
(that said, "Don't design based on performance requirements" is a crazy thing to say in a language used heavily by game developers and high frequency trading. Like, if you're using C++, it's probably because you're in a domain where realtime deadlines are some of your first and firmest constraints. I just... I can't believe I saw that on a slide. That's wild to me)
Its interesting talk but I ended up disagreeing the talk right for what you say here "Don't design based on perf req" - like wtf? If you use C++ you already designing for perf by language choice at this point... and virtual functions and inheritance is the most non-zero cost abstraction in the language so why not avoid it? In most cases the variant like structures are easy to manage and its not like template libraries are so wrong...
Maybe instead of trying to save the OOP grift, the language should move and solve faster compilation for header-only stuff? I think its more beneficial than trying to convince us we should not design for perf. Also as a "perf guy" I never seen really good perf refactors ever..... things that are fast more often than not designed from the start to be fast (yes not fully micro-optimized, but totally architected with perf already in sight in ones brain)
The lack of performance mindset is endemic though, I work in a domain largely dominated by python, and I've re-written closed source c++ tools in python and they've run far faster. I honestly cant imagine how its even possible to make c++ that slow but I've seen it with my own eyes.
I'm only learning c++ now, but what it feels like from 'modern c++' is the focus is on making it safer and slower, when I read docs for Rust, Odin and Zig I see them trying to make it safer to be fast. I am however new to this and could be talking utter bollocks.
A REALLY excellent talk. 🎉
CRTP, mixins, sum types, and the expression problem.
The question of constraining a concept to avoid unwanted duck typing is an interesting one that I’ve thought a lot about. I find whenever I go to stub out a new concept this problem seems really important to avoid, but once I’m done with the interface I find it impossible to believe that something could accidentally match.
When it’s absolutely necessary, but you really want to avoid any semblance of inheritance at all costs (rightfully or not, as this talk calls out), a separate approach to a tag base class is to define a keyword type aliases within the class. Something like:
Concept foo = requires(T t){ typename T::traits::is_foo; }
struct fooable {
struct traits { static constexpr is_foo = true;
}
}
This is gross for a million other reasons, but it’s a powerful tool for opt-in adhoc concept matching without necessarily being bound to a hierarchical type inheritance structure.
There's Traits class. Check a book about Boost Graph library, there's in chapter 2 a lot of information about such technique.
I think the problem with oop is that its used... Weird...
Imho the example with shapes shows a problem how oop is taught: shapes is data and the drawstrategy is the functionality!
Why should a shape draw itself? Why should a data know, how it is used?
We could use inheritance for the drawstrategy and a templated vistor approach to pass the shapes to the drawer.
This separates data and functionality which helps structuring the code. And should be good for performance too since shapes can be places continuously in memory and the drawstrategies (hot code) can be placed in cache just fine.
54:36 the c++ standard requires std::visit with a single variant to have a constant time complexity with regard to the number of variant-alternatives. I believe that the poor clang (libc++) performance in slide 99 (42:17) was related to their choice of writing as simple code as possible that adheres to that requirement
Klaus mentions my talk at CppCon '23 where I mentioned deducing this. I did compile the code using Compiler Explorer MSVC which was the only compiler that supported deducing this at the time. I never got into using it since it wasn't generally available for my Linux system.
Nice one!
Great talk!
34:00 "always had templates"
That should give the age of the author of that quote. I remember reading Bjarne's papers on template design -- the way they are was not the first iteration. I remember making a special text preprocessor to give me parameterized types, so I could make the equivalent of vector.
Now we just need a talk explaining how to get the best of all world. A data oriented way to do that, with great performances, keeping the type erasure and keeping the dependency injection possibility. It does involve templates but not in a "contagious" way and thus propagating to all the code. However it is probably the most complex code of all.
Edit: Also it would not allow dynamic multiple dispatch. So for example having a plugin (not known at compile time) introduce a new kind of shape/animal would not work. Both your code and my code would need to be present when compiling the main application.
if we had a tool that finds or collects implementation types in a header file, close set of types for variant wouldn't be a problem. But the tool has to look at all related .hpp and .cpp files before compiling or before preprocessing. IDK any such tool. Maybe someone knows?
* std::variant generates a vtable for each call to std::visit
* One way of dealing an open set of operations together with an open set of types is to use type erasure, and that was what Sean Parent actually ended up with. You can bind by value (smart pointer), or by reference. You may have a class template that implements the interface as in the talk. You can also design you vtable on your own with raw function pointers, if you want to control memory layout. You can even use some data oriented design, with parallell arrays for function pointers and object pointers.
Small correction, std::variant does not generate vtable, but something more like jump table / switch table.
@@MarekKnapek It is HUGE correction, because the variant way of doing this means much more cache locality and also generally just cheaper to branch than to call an address too...
15:55 There's a term "static polymorphism". Isn't it basically a "static interface"?
This is another great talk. The disambiguation of CRTP in terms of static interface and mixin is very helpful, and all of this was presented very clearly. The end is actually a bit of a cliffhanger that is resolved in another recent talk by Klaus at cpponsea (th-cam.com/video/m3UmABVf55g/w-d-xo.html), where he cites a "value-based OO" solution based on type erasure as possible alternative to virtual functions.
Feel sad that CPPCON don't have note in these ppts
CRTP is badly extendable. If you would like to have nested CRTP-interfaces, you would have additional template parameters, and it looks more horrible.
C++23 deducing this would solve it.
A very interesting talk with which I wholeheartedly disagree. You complain the alternative ways create header-only libraries - but honestly I pretty much WANT only header-only libraries (even better if they are single-header ones tbh). Also see no issues with that kind of code whatsoever - especially compared to the so bloated and talky OOP. Yes there are everywhere, but still less magic than what virtual functions do.
Also: You can go and literally use more function pointers instead of virtual if this becomes too complex - and you will realize how easily the optimizer can optimize them away if done right and apply function-abstraction. If you want, you can even utilize some macros... literally even hard macro magic is better than OOP at this point.
Who thought they were? 🤔
Thought what were what?
Did the crowd really moan when he said virtual functions?
Perhaps Rust traits is a solution for these problems :)
Good talk, but tbh the problem with dynamic polymorphism and all forms of type erasure is just a lazy design decision. We all know(or should know) what problem we're trying to solve, what types we're using, what time/space constraints we have, so just do it. Future proving it brings nothing to the table, especially if there are baked in performance pessimizations already in. There's no point pretending that allocating and writing to memory and creating a file and writing to it are the same just because the types have the same interface. Type erasure is just premature pessimization, you know the type, but you're willingly forgetting it. The obvious design choice is to have N vectors of a specific type instead of a vector of a base class which has N types deriving from it. Abstract only if there's no other option.
It can be very valuable for testing code, via dependency injection. And the virtual method call is very ergonomic, if you can afford the slowdown. For many applications, the performance cost is almost 0 compared to other operations it does and so the convenience and nice mental model is a win.
Hi, I think one point of Software Design is that although “we all know what problem we’re trying to solve” we do not know what problem we’ll need to solve 3 years from now - and we can benefit if we still plan ahead and try to write code that will age well even as the problem changes
@@Roibarkan If you don't know what problem you'll need to solve 3 years from now, how could you possibly plan ahead? What you risk is creating a "future proof" design that doesn't fit the future problem that you would then need to work around, thus creating more problems than if you did absolutely nothing at all until the facts were on the table. And the more time you spend on "future proofin" the more time and money you spend on devlopment in general, time and money that could either be spent on just making the current design more robust in handling the problems you do know, or just spent on other projects or anything else really.
I bet that ending up with switch or multiple if statements in many places is equivalently ugly
@@Roibarkan I agree in principle, but in my personal experience, trying to predict what changes would be needed in 3 years never worked in practice. Maybe it's just me lacking foresight, but it's always been short term iterations that drove projects forward, and having rigid interfaces was more of a hindrance, like having 5+ people needed to approve my pull request. Could be I'm just an old fart and need to retire soon, but I think that throwing out useful information(like the actual data type) is not really a sound design principle.
Why not just use the term that has been used for what, 20+ years, instead of static interfaces - it is a mixin!
wasn't his point to differentiate between two types of CRTP, with one being "mixins" and the other what he calls "static interfaces"?meaning static interfaces are by his definition explicitly NOT mixins?!
He used mixin for one use case, and a different term for the other use case.
20+ years? It actually goes back to LISP with CLOS, with an ANSI standard released in 1984. The concept and name was probably used with older predecessors of CLOS, to the late 1970's at MIT.
@JohnDlugosz see, it's even older than that 20+ years, which sort of was the point.
@@JohnDlugosz Hey John! It has been awhile since I've seen your name.