Breaking Dependencies - C++ Type Erasure - The Implementation Details - Klaus Iglberger CppCon 2022

  Рет қаралды 31,191

CppCon

CppCon

Күн бұрын

Пікірлер: 60
@9uiop
@9uiop Жыл бұрын
This guy knows a ton about design yet he explains stuff so simply and understandable even novices can a grasp on his topics. Great talk again Klaus!
@xealit
@xealit 11 ай бұрын
A really brilliant presentation. And Klaus' book "C++ Software Design" is great, by the way. But, after several design patterns, template construction of a concept, with implementations inside hidden friends - how is that KISS? Especially in comparison to the standard and really basic language feature of inheritance? Klaus says "maybe inside it's not that simple, but for the user it's nice" - imagine a user looking at these classes to find out how to extend something (the user has to get to the hidden friends), or just to understand what the interface does. In principle, the user just has to know the design pattern, and preferably everything that goes into it.
@Niohimself
@Niohimself Жыл бұрын
The most surprising part of this technique, at least to me, was that the Square doesn't know it's a Shape, or that it can do Shape-ly things, but we do. So that means several people can have a different idea of what a square can do (different ShapeConcept, different number of related free functions) but since all that variation is taken out of the Square, now everyone has the same idea of the "Square itself" so they can send Squares to each other.
@joeedh
@joeedh Жыл бұрын
You don't think that's a bad thing? Seems like it would encourage organization balkinization.
@weiqiu504
@weiqiu504 Жыл бұрын
seem like duck typing in python
@Roibarkan
@Roibarkan Жыл бұрын
43:22 note that std::is_trivially_copyable can (should) be used to verify if copy of the buffer is equivalent to copying the objects. Technically, one could use “if constexpr” in the assignment operator to choose between buffer operation, affordance dispatch or compilation error.Great talk.
@IasonNikolas
@IasonNikolas Жыл бұрын
Adding final in the ShapeModel inheritance might help the compiler to better optimize in some cases.
@jamesbond0705
@jamesbond0705 Жыл бұрын
A possible typo report: From slide page 56-68, the term "Concept" should be changed to "ShapeConcept".
@MaitreBart
@MaitreBart Жыл бұрын
I was asking myself the same question: where is this Concept base class coming from?
@privateerburrows
@privateerburrows Жыл бұрын
External polymorphism was the motivation for the good old Visitor Pattern; but this seems far better, with things able to BE more than one thing without incurring multiple-inheritance.
@TheCSarmat
@TheCSarmat Жыл бұрын
Looks like there is a performance issue (time 54:00) related to function "void draw( ShapeConstRef const& shape)". It seems that it would be more efficient code if the function was without const& "void draw( ShapeConstRef shape)" because here we have double-level of pointers dereference. Am I right?
@vegaforce2539
@vegaforce2539 2 ай бұрын
I appreciate the effort but this is the opposite of KISS. So we replace inheritance with a template class the inherits from a base class to create a wrapper around “any” shape? It may only be me but this is confusing as hell. Bring back inheritance then.
@IasonNikolas
@IasonNikolas Жыл бұрын
@CppCon at 21:18 wouldn't be more wise to use the strong exception guarantee version of "Shape& operator=(const Shape& s) { return *this = Shape(s); }" ? I don't quite get why I have to swap the pimpl object instead of the actual Shape.. Isn't it possible for the Shape class to have more state, or only pimpl is allowed in that pattern?
@firejox4982
@firejox4982 Жыл бұрын
I found this is useful for unify different IO type, e.g. stdio, socket, pipe, named pipe. These IO types have different ways to construct and storage different information. And that information are not important when we use on reading or writing data. All we care on IO types are reading and writing.
@myusernameislongerth
@myusernameislongerth Жыл бұрын
Hm, Sean Parent did this talk some years ago
@vladimirkraus1438
@vladimirkraus1438 Жыл бұрын
At @12:43, should not there be shape->do_draw() instead of shape->draw() inside drawAllShapes()?
@jamesbond0705
@jamesbond0705 Жыл бұрын
Yes. The speaker fixed the naming conflicts in his 2021 CppCon talk here, but he forgot to apply that change to this part.
@danielmilyutin9914
@danielmilyutin9914 Жыл бұрын
I believe MVD got slower in the end because pointer to function construction and call of function pointer has its cost as you create those temporary ShapeRefConst objects.
@Roibarkan
@Roibarkan Жыл бұрын
It’s obviously hard to hypothesize without knowing some details of how Klaus implemented SBO+MVD. However, I assume such an implementation won’t have a non-owning ShapeConstRef (like the basic MVD) because SBO implies ownership. I assume the SBO+MVD would be similar to SBO except the buffer inside the shape will directly have (placement new) the actual specific shape-object (no ShapeConcept/ShapeModel), and apart of that buffer every Shape will have a function-pointer (initialized to a lambda) that correctly static_casts the buffer and calls its free-function. My assumption regarding the perf loss is that perhaps the pure MVD solution had a more compact memory usage (4 vectors of the different types of shapes, and a vector of ShapeConstPtr’s that point into them) while the SBO+MVD was a little less compact because 128 bytes are much larger than the actual space needed. Again, great, thought provoking talk!
@danielmilyutin9914
@danielmilyutin9914 Жыл бұрын
@@Roibarkan I'd like to ask you a little offtopic question. What bugs me is creating temporary view objects takes its toll. Imagine matrix library and you want view to row/column and do some operations with it. My tests showed that creation of this temporary object eats too much compared to call some row_fcn, col_fcn. Have you experienced something similar? What did you do to fix this?
@abhinavk0929
@abhinavk0929 Жыл бұрын
I was waiting for this talk!!!
@chrisminnoy3637
@chrisminnoy3637 Жыл бұрын
This video takes it slow, so good for novices. Be aware that this is a simple example of Type Erasure, don't think by watching this excellent video you know TE.
@georganatoly6646
@georganatoly6646 10 ай бұрын
got to be honest, that was pretty rough, trying to find silver linings; it was interesting to see the compiler view through the benchmark results he showed, it appeared like basically the compiler just threw out all that extra stuff, like, if it was obvious the compiler cared one way or another than maybe it could have some value as a potential pattern or anti pattern, but no difference from classical inheritance, that's a bit rough
@BalanNarcis
@BalanNarcis Жыл бұрын
That was fast, good job guys! Great talk!
@nice_sprite5285
@nice_sprite5285 Жыл бұрын
Are the 2500 translates() per shape object, or per shape type?
@miropalmu5588
@miropalmu5588 Жыл бұрын
Excellent talk!
@shelper
@shelper Жыл бұрын
is there source code available for this talk?
@IasonNikolas
@IasonNikolas Жыл бұрын
Did anyone try to use std::shared_ptr to store pimpl object instead on std:unique_ptr? This would remove the need for the clone function inside the concept and any copies will be eliminated! Also the default special member functions will work as expected and there is not need for user defined copy ctor/operator etc. I expect this approach to be more efficient than the simple approach.
@alexeysubbota
@alexeysubbota Жыл бұрын
But the thing is that we want to have a copy! In your case we just have copy a pointer to ShapeModel but not copy of ShapeModel itself
@Tibor0991
@Tibor0991 Жыл бұрын
If you're copying, the intention behind is that you want to "fork" the lifetime of the original object; with a shared_ptr, you'd be creating two instances of the same class which point to the same "physical" object (in other words, the same memory area).
@GrzesiuG44
@GrzesiuG44 Жыл бұрын
This is perfectly valid thing to do when all your operations are const - which you also correctly modeled with the const internal type. There are definetly great use cases for that approach as well: my personal example of choice is a generic id type, for which you can even short-circuit the equality operation and skip virtual call if your pointer points to exactly same pointer. I would say this concept of type erasure - although focusing on gaining value semantics - was explored more than 10 years ago in talk "Value Semantics and Concept Based Polymorphism" by Sean Parent (or "Better Code: Runtime Polymorphism" - that one has better quality).
@IasonNikolas
@IasonNikolas Жыл бұрын
@@GrzesiuG44 Great talks thank you for pointing them out to me. I was just 10 years late!
@joeedh
@joeedh Жыл бұрын
I've watched this video twice. I just don't find it convincing. There's a lot of talk of how everything is better theoretically, juxtaposed with code that is so much worse. Definitely the kind of thing I'd only consider after extensive profiling.
@KeyT3ch
@KeyT3ch 6 ай бұрын
Essentially, any struct/class can be a "Shape" as long as it implements the interfaces that Shape requires, and that is amazing, that's what interfaces should just be, without inheritance. This is effectively Go's Interface
@simonmaracine4721
@simonmaracine4721 6 ай бұрын
Great!
@pawello87
@pawello87 Жыл бұрын
In classic OOP I can do: auto my_circle = make_shared(2.5f); shapes->add_shape(my_circle); // add_shape accepts base class ptr, ex. shape* my_circle->set_radius(1.0f); I can store that pointer and modify this concrete circle any time i want In this type-erasure implementation: auto my_circle = circle{2.5f}; shapes->add_shape(my_circle); // my_circle is moved-out my_circle.set_radius(1.0f); // use-after-move! Well, I'm losing any possibility to modify and even access that circle instance. Conclusion: I prefer to leave lifetime responsibility to the user than give him a fake simplicity by the cost of limited access to his type. I see a lot more advantages in type-erasure based on shared_ptr.
@blacklion79
@blacklion79 Жыл бұрын
Classic OOP doesn't care about value types and «loves» mutability. Modern OOP cares about value types and tries to avoid mutability, because non-concurrent programming becomes less and less relevant and concurrent mutability is very error-prone and non-scalable.
@pawello87
@pawello87 Жыл бұрын
@@blacklion79 Ok, but you lose access to *any*, even non-mutating method.
@junekeyjeon1124
@junekeyjeon1124 Жыл бұрын
Not quite. my_circle is copied, not moved. The std::move you see in the constructor is for moving out of the parameter, not the argument passed. The argument will be either copied or moved into the parameter depending on the value category of it, which is determined by how you call the constructor. In your example you passed my_circle which is an lvalue, so it gets copied into the parameter (which in turn is moved into the newly created shape). On the other hand if you wrap your my_circle with std::move, then it will get moved into the parameter (which in turn is moved again into the newly created shape). There is no use-after-move in your example.
@rationalcoder
@rationalcoder Жыл бұрын
I don't like being rude, but what the heck, man. This is basically trying to make up for not having the interface model of Go in C++, but it's almost never worth doing. I _have_ found something like this useful in situations like defining something similar to MetaTypes in the Qt framework (but in my own code) and even that was just to be able to define components of external types that have no knowledge that they are components. Also, boiling types down to void* and size/alignment or whatever internally _is_ in fact type erasure. Anytime you have types in, no types internally, and types out, you can generally call that type erasure.
@christiandaley120
@christiandaley120 Жыл бұрын
37:20 I believe that std::aligned_storage_t would be better suited for this rather than std::array
@idanaharoni8401
@idanaharoni8401 Жыл бұрын
aligned_storage is deprecated in C++ 23
@antagonista8122
@antagonista8122 Жыл бұрын
Since C++ introduced alignas it's not necessary to use types that depends on compiler intrinsics (e.g. aligned_storage_t). Both alignas array and aligned_storage_t have equally non-intutive API (user has to explicitely use reinterpret_cast and placement new) so it doesn't really matter. + aligned_storage_t is deprecated since C++23 due to terrible API - ^ + users often incorrectly use aligned_storage type directly instead of aligned_storage::type/aligned_storage_t alias as they supposed to do.
@sqlexp
@sqlexp Жыл бұрын
This presentation doesn't give C++ a good reputation. Unless you are stuck with using C++14 or earlier, you should just use std::variant if you can provide the list of shape types ahead of time. Just replace std::unique_ptr or the aligned array with std::variant in Shape class, and use a switch statement to dispatch the call to draw/serialize. There is no need for ShapeConcept and ShapeModel. There is no additional allocation from the heap, no placement new, and no virtual function call.
@isodoublet
@isodoublet 11 ай бұрын
He addressed this exact point in the Q&A. They do different things.
@maelstrom254
@maelstrom254 Жыл бұрын
C++ is dead to me, if such trivial things require so much effort 😢
@alexeysubbota
@alexeysubbota Жыл бұрын
I didn't understand what this talk is about. There were many classes in which it is easy to get confused. It was hard to keep them all in mind. Maybe a class diagram could've helped understanding. I was tied after watching a half of the presentation. Without an example of problem I didn't understand what the problem he was solving... After viewing, there was a feeling of overcomplication
@alexey104
@alexey104 Жыл бұрын
The first part of this talk describes the problem with the traditional approach using inheritance: kzbin.info/www/bejne/apbIdoaAhtBgo9k
@guenterscherling9069
@guenterscherling9069 Жыл бұрын
Actually, C++ including templates does not need all this C++11/C++17 etc. stuff. The consultants need it. C++ is going to be destroyed.
@Peregringlk
@Peregringlk 9 ай бұрын
Inheritance is a bad design pattern. This talk is about getting dynamic polymorphism WITHOUT inheritance exposed in the interface.
@usrnm9076
@usrnm9076 5 ай бұрын
Skill issue
@gregwoolley
@gregwoolley Жыл бұрын
Nice talk, but the loud sibilance made it painful to listen to. Need better microphone to tone down those super-loud 'ssssssss'.
@treyquattro
@treyquattro Жыл бұрын
I believe type-erased C++ is called C...
@chrisminnoy3637
@chrisminnoy3637 Жыл бұрын
Very far from it. C should be labeled deprecated.
@maelstrom254
@maelstrom254 Жыл бұрын
@@chrisminnoy3637 rather C++ is deprecated, if such trivial things as Shape require such insane amount of effort to implement 🤯
@chrisminnoy3637
@chrisminnoy3637 Жыл бұрын
@@maelstrom254 sure, whatever you say bro, if you want to stick to 8 bit processors and only simple logic to run.
@Adowrath
@Adowrath Жыл бұрын
@@maelstrom254 What do you mean "such trivial things as Shape"? Is "an extensible range of types that all share an equally extensible set of operations such as drawing and serialization" just trivial to you? Do you need to create new such hierarchies/sets of types every day in your programming language?
@PaulMetalhero
@PaulMetalhero Жыл бұрын
Generally nice design, but that first optimization is nasty. Manually calling a destructor? No way
Players push long pins through a cardboard box attempting to pop the balloon!
00:31
Симбу закрыли дома?! 🔒 #симба #симбочка #арти
00:41
Симбочка Пимпочка
Рет қаралды 4,5 МЛН
C++ Type Erasure Demystified - Fedor G Pikus - C++Now 2024
1:40:57
Back to Basics:  RAII in C++ - Andre Kostur - CppCon 2022
44:17
Branchless Programming in C++ - Fedor Pikus - CppCon 2021
1:03:57