Better Code: Runtime Polymorphism - Sean Parent

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

This is a nice design pattern. I was skeptical if I would like the talk, but it was pretty good and I learned something new.

👍︎︎ 3 👤︎︎ u/[deleted] 📅︎︎ Mar 25 2017 🗫︎ replies

You can find the slides here.

👍︎︎ 2 👤︎︎ u/mariobadr 📅︎︎ Mar 25 2017 🗫︎ replies

This talk is a gem.

👍︎︎ 2 👤︎︎ u/louis_dionne 📅︎︎ Mar 25 2017 🗫︎ replies

He is wrong in one thing: C++ compilers do devirtualization, C9 Going Native had a episode about that (final vs override helping compiler).

👍︎︎ 1 👤︎︎ u/Z01dbrg 📅︎︎ Mar 27 2017 🗫︎ replies

Is this some kind of joke?

This so called pattern uses inheritance, polymorphism, heap allocation, and it is not even thread safe!

And it is not even extendable! if concept_t is private to document_t, how would an external module provide new types???

👍︎︎ 1 👤︎︎ u/axilmar 📅︎︎ Apr 12 2017 🗫︎ replies
Captions
so I'm Shawn parent I'm a prince scientist at Adobe Systems out in California I've been at Adobe now depending on how you count about 22 years I started my career there on Photoshop and now I'm actually back working on Photoshop again but I did run a research team at the company for about eight years with Alexander stepping off on the team for people who who know who alex is he's the person who created STL um this is one of my favorite talks that I give on runtime polymorphism but with five people in the audience here if you guys want to hear about something else just raise your hand and I'll talk about anything I'm here for you guys so a while ago let's see I guess it was about three years ago now at the going native conference at Microsoft I gave a talk called C++ seasoning and I got a lot of requests after that talk to write a book so my agreement has been that as I get asked to do additional talks I try to flesh out a chapter from the book so this is kind of the outline of the book and this talk fits in about here it doesn't matter if you haven't seen seen the other talks I'll give you the background information this just gives you a little bit of background now all my talks have a goal the goal for this talk is to write code without inheritance the reason why I state the goals in the negative there non-prescriptive right I'm not trying to tell you what to do instead although I'll present some alternatives here but I'm going to present the problems that inheritance cause causes and and it's I as a goal it's an open-ended issue so it's like it's like strive hard to not use inheritance but if for the problem you're trying to solve it's the best tool you've got in your toolbox then use it so it's not a rule it's not a guideline it's a goal so what is inheritance right so inheritance is a mechanism to implement runtime polymorphism where one class is derived from another class but overrides all are part of that implementation okay now here we're talking about class inheritance there's other forms of inheritance there's protocol inheritance but in this talk we're talking about class inheritance a quick disclaimer in my example code to keep the code readable I I'll leave out header files inline function namespaces things like that they're just ignored for clarity so I've a little IDE here for my slides so we can write main and this will output hello world that's the idea here so we're going to develop a little library in our library we've got some object type which for now is just an integer okay and we've got a function that can draw our objects by just streaming out our integers okay we've got a document type which is just a vector of integers sector of our objects and we've got a draw function to draw to draw our objects right so fairly simple that's my library some client code might look like this we create a document we add four objects to it and we draw the document and that will output this okay so some quick guidelines here right all code as if it were a library right right reuse of your code increases productivity for you and your entire team and writing unit test is simplified if things are factored out into a separate library so what happens if we want our document though to hold any drawable object how do we go about solving that right right now it's just holding one type of object well the traditional way is we say okay for any type of object will make object via base class and it'll have a V table under the hood okay it's going to point to a virtual destructor and our virtual we're going to virtualize our draw function okay and now our document can hold multiple objects right my class type key some other type key right but I can't store them directly in my document because they're variable size male right the different instances of different types of of my objects in my document are different sizes so what I'm going to store in my document are pointers of some kind right so my library now looks like this okay so I've got a base class for my object type I've got my a virtual destructor okay I've got my document type to manage the memory I'm going to use a shared pointer right that's what most people do right there and so my client code now I can write a subclass and I can output my document right I can put in my own type right there and I can draw it okay so everybody following the code fairly simple so how many people see the bug anybody now I don't have to declare it virtual because it's in the base class it's virtual yeah it works I can run it but there's a bug there's a bug right there okay so what happens is I come along and I knew my object I called in place back to put this into my document document has to grow its size growing its size can fail that would throw an exception okay I already allocated my new my class it's not yet held by a shared pointer and it leaks okay so there's a small bug right there so that's why most people in the C++ community will tell you never call new directly okay just don't okay instead call make shared okay so this is a actually also more efficient for another reason if I call make shared then make shared is going to allocate the reference count for that pointer along with the object in a single block as opposed to putting the reference count in a separate block if I just need the object okay so I have two wins there that's exception safe and it saves me a little bit of memory and a little bit of time okay but there's a deeper problem with this piece of code right what happened here is I change the semantics of what it means to for copy and assignment and equality of my document okay before when I had my document and it just contained integers I could copy it it copied all the contents now it's copying pointers to all the contents so I've got shared data okay so you see applause and then programming in general we define an operation in terms of the operation semantics so assignment is a procedure taking two objects of the same type that makes the first object equal to the second without modifying the second that's the definition of what assignment means if I look at the structure I've created I have shared pointers in in my document now pointing to a single instance of T and if I think of those as the individual types right I can copy my shared pointer that obeys the rules for assignment okay but what I fail to account to is the relationships the arrows that those pointers point to something and so if I take those into account then I have intercept expecting objects okay those intersecting objects can interfere with each other so really I have a single object that's this that's all instances of the shared pointer and what they're pointing to okay so the structure is still copyable undecidable but we have to do it through other means the shared structure also breaks our ability to reason about code right anytime I'm reading a piece of code and I see a shared pointer and if you're coming from a JavaScript world or a Java world or or a dotnet world this drives me nuts and what I refer to as the reference semantic languages right which is all of those those languages every time I see an object I do not know who else is sharing that object right so I don't know when I call a function what are other effects that function is going to have on the object that I'm hanging on to so I always say a shared pointer is as good as a global variable right so we want to choose the same syntax to have the same semantics that enables code reuse and avoid common toriel interfaces this is what the entire SPL is built upon is this principle okay so if a.type has a proper set of basis operations can be adapted to an alternate set of bases operations regardless of the syntax right so if I have if I have the ability to copy an object then I have the ability to to map that copy into assignment or into a copy constructor right now C++ has defined semantics for operations on built-in types including assignment copy equality and address of ok ok now it's interesting copy constructors are actually things that I see abused the most I actually had an engineer walk into my office one time and said so I'm writing this class and I want to be able to copy the class so what do I call the member function that does copy and I said well that would be the copy constructor and he said no no I'm already using the copy constructor for something else this is a horrible situation because the compiler understands the semantics of the copy constructor and the compiler canalize your copies so if you're using your copy constructor to do something without copying then you really don't know what's going on in your code ok so the book elements of programming we say this there is a set of procedures whose inclusion in the computational basis of a type let's just place objects in data structures and use algorithms to copy objects from one data structure to another we call types having such a basis regular since their use guarantees regularity of behavior and therefore interoperability right so a regular type where the regular operations are implemented with the standard names are said to have value semantics copy assignment equality right those are our standard basis operations when objects are referenced to indirectly through a shared reference or a pointer the objects are said to have reference semantics right so shared pointer is reference semantics an object in JavaScript is reference semantics an object in in Java or or in c-sharp reference semantics you okay the next deep problem with our code here when we went to the shared pointers is the call to draw on my class TR often interacted through virtual calls including the destructor even when it's not necessary okay I'm always allocating my class T and access to my class team must be synchronized now if you're programming in Java some of this the compiler is able to optimize around from you because the compiler actually understands reference semantics so it's able to do things like D virtualization and move things into into scoped memory if it understands your code in C++ it cannot the compiler does not understand reference semantics and nor can it under the current rules of the language so the compiler cannot avoid heap allocation sometimes the compiler can be virtualized the calls another problem here polymorphism is intrusive right the document that we created here now that holds our object key based class I can no longer put in a simple integer right right I can know right before I could so even if I have a draw function on my integer right my an my integer is a drawable thing but it would have to inherit from object e and that means that if I want to use somebody else's class from their library I have to put some wrapper around their class to turn it into a drawable object right in large systems this becomes a huge problem right what you start to see is every header file includes a ton of header files in every object inherits from a whole bunch of things and the problem stems from our use of the notion of the term polymorphic types right all of these problems the requirements of a polymer 'fuck type by definition comes from where you use the type okay not from the type itself right so an integer is not a polymorphic type okay my integer might be drawable okay what I'm saying is there is a piece of code within which I want to deal with a set of types such as integer and my object key based class and whatever else I create okay that share a particular attribute so I want to handle those objects as if they were the same that's what we mean by a polymorphic type so when I use inheritance to represent that relationship what I'm saying is I'm building the use of my objects into my objects okay and what I want to do is separate the use of my objects from my objects themselves so there are no polymer types only polymorphic use of similar types exactly what I said we're tightly coupling components inheritance implies variable size which implies heap allocation heap allocation forcers a further burden to manage the object lifetime and shared pointer unique pointer all these things indirection heap allocation and virtualization end up impacting my performance of you lifetime management leads to garbage collection or reference counting this encourages shared ownership and the proliferation of incidental data structures what's an incidence incidental data structure it's when I have have a data structure within my code where I have no single object that points to it so if I have two shared pointers within my code holding on to some element somewhere else that is a data structure but there is no collection of shared pointers that represents that that's an incidental data structure shared ownership leads to synchronization issues right right if I'm start trying to share these things across threads it breaks my ability to local reason there's good as a global variable and further impacts performance which leads me to this line inheritance is the base class of evil so let's do something instead let's take our library here and let's start mutating it ok this is back to the original where our object is just an INT okay so the first transformation we're going to do is we're going to wrap that int into a class okay so all we did here is we've got our object key and in cells down there at the bottom holds on to it we're going to have a draw function up on top that just knows how to draw integers okay and we'll declare a friend function for drawing or object type that just knows that in order to draw this object you're really just going to draw the integer it contains okay so all we did was we put a wrapper around the int okay that was it okay we're going to let the compiler supply a copy an assignment operator which is perfectly happy to do in this case and that means that we can still write the same code and it still outputs the same thing right transformation number one so by writing a class that behaves like a regular object we can increase its reuse okay so next transformation let's take our object now with our int and let's heap allocate the int okay so we're just going to let add a level of indirection we'll store it in a unique pointer we'll use make unique so we don't have a memory management issue okay so we're still just holding now instead of holding an inch we're holding a pointer to an end simple enough okay do your own memory management don't create garbage for your clients write this all the management of this this pointer is inside of our class that's where we want it right now have to add our own cotton constructor here now we can't rely on the compilers okay so we'll add a copy constructor and we make sure there's actually copies copy copy the objects are equal and disjoint that's what copy means okay we're going to add an assignment operator okay so we're going to write our assignment operator in terms of making a copy okay and then do you mean the guts of that copy right we're moving it here this is kind of a scattered standard pattern even though it's just an integer move is the same as as as copy for just that int I'm sorry that's not true because we've got a unique pointer at this point so we're moving the unique pointer over and spilling the guts from the copy okay so assignment is consistent with copy generally if I create an object and then assign to it it's equivalent to if I can't be constructed okay so this assignment satisfies the strong exception guarantee that's a nice property what do I mean by that I mean if we throw in the middle of trying to make this copy we end up leaving the object in the state it was in originally okay basic exception guarantee is that the object is is still satisfies its invariants okay but it's in an unknown State that's a whole separate talk okay assignment like all other operations must have supply the basic exception guarantee and you notice we don't optimize for rare cases like self assignment right sometimes you will say say you know if this thing is really if I'm assigning it to itself right because that can happen through a reference then don't do anything okay so this would actually go ahead and make make the additional copy but self assignment is incredibly rare and so the check to see whether or not you're doing a self assignment in your system is slowing down the 99.9999 case in favor of optimizing the point zero zero zero one case so don't do things like that okay so our code now same code still works right so doing this kind of separation sometimes you want to do it in code it's referred to as a pimple pimple pattern or handle body idiom okay this can be useful for separating the implementation of your class from the interface so sometimes you can have a private implementation here our implementation is just an integer but that could be some class sitting into that CPP file and I want to keep all those guts out of the header files right structuring my code this way putting an extra level of interaction is one way to do that okay let's take a look at something here we're going to decorate our code it just so it tells us when we're doing construction and when we're doing copies and here's old piece of code so what's going on here we've got a function that constructs an object assigns it the value five returns it as the result okay and then down here we have object X which we assign from function okay so our assignment remember is written in terms of copy so what is this going to print director only anybody else so he's correct and almost every compiler this will print this it's not guaranteed in the current standard but it will be guaranteed in C++ 17 okay so right now this is called a return value optimization and the way it works is that because I'm constructing object X here my function reason well my object key the result of a function actually lives in the scope outside of that function and so the compiler knows that if it's constructing a result and that result is going to be used to do the return okay that's a named value that it can construct it directly in place in the object T this is what I mean by the compiler understands what copy means and so the compiler can elide copies so if you have your copy constructor doing something else you don't know what your code is doing because you don't know when when the compiler is going to call it or not okay so let's change Oh just a little bit we'll construct an object with X and then we'll assign it from functions question yes correct so which we couldn't be because the result is the local variable so okay so what will this print anybody then copy very good so where'd that copy come from assignment the copy came from right here we actually wrote that copy right the first statement in our assignment operators make a copy so that's our copy okay okay so we're going to rewrite our assignment in this form okay what we want to do is say don't take our assignment in terms of a Const reference take our assignment by value that actually hoists that out of the scope okay and then we'll just move it into place okay the general guideline here is to pass sync arguments what's a sync argument a sync argument is is is an argument which is going to be stored or is going to be returned from the function that we're passing it to okay so if we're taking something and we want to to basically move it somewhere else or we turn it which is a form of moving it somewhere else we want to test it by value okay the argument to assignment is a sync argument so now that will print seat or seat or okay we got rid of the copy why because we lifted okay so in our assignment we lifted out the argument and now the compiler is able to do return value optimization from the value coming out of there into the argument of X get moved into place okay so now let's go back to our document like this and we want to reserve right right just so that we're not seeing copies from our document growing and then we're going to reverse the document right and reverse is going to do care wife swap all the way down so what's this going to print anybody so we're going to do two pairwise swaps you know so we got our structures copy copy copy copy copy copy copy write all those copies are are copy to a temporary and then copy across in our pairwise swap right provide our own swap and we didn't provide now a move constructor for the compiler so what we want is a move constructor right which is the double ampersand there so we're taking an r-value reference right and our value is a temporary swap in people of plus eleven and fourteen is written in terms of moves so with move since we know that the value being passed to us as a temporary so or the user's cast it to a temporary with the move expression which is what move does then we can just steal the guts of it now that to just move all of our members is actually the default so all we have to do is say this okay let's plus eleven and fourteen this will get picked up by swap we'll print that okay so we got rid of all those copies now here's a tricky one what does this thing print okay all we did is we took our object key and we wrapped it into a struct okay and so now our our construction we construct it with zero and then we assign through our function there okay so same code as before it's just wrapped one level destruct our copies back okay this one drives me nuts this is because of what I consider a language defect okay for the move assignment operator a non static data member direct base with the type that does not have a move assignment operator and is not trivial okay so the compiler will not provide a move assignment operator okay on our some type key unless all of its members our move have a move assignment operator now the reason why that's a flaw is if that sentence there said said instead of saying does not have a move assignment operator said said is not move assignable then our copy would go away okay so because our code is move assignable it just doesn't have a move assignment operator which has a very particular signature so we can't quite get away with that so instead we need to put back our copy constructor and default our move assignment operator which will just move all of our members which is a unique pointer so that's going to work fine okay okay so we want to pet so we're going to amend our rule here we want to pass sync arguments by value and swap or move into place a sync argument is in the argument consumer return for this function the argument to assignment is sync argument however because of a language defect you must write them will write a move a segment operator okay now our copy constructor now that we have a move assignment operator we can actually shorten our copy constructor to that which is make a copy which is an r-value and assign it into place okay so we fixed that passing read only arguments passing are values of sinc arguments do not require a copy understanding this can greatly improve the efficiency of your application okay so if you didn't know all of this these are kind of important things to learn so let's get rid of that and what should we do next year okay the observation here is that our constructors before they were taking constant acts the constructors are frequently sink arguments okay or the arguments to constructors are frequently sink arguments and so we're going to go ahead even though they're just integers and it's going to copy we're going to pass them by value and move them into place just to follow our own rule okay now we want to make this polymorphic so up till this point it's we've only held string or we've only held integers now we wanted to hold strings so we can construct our object with a string we'll do it the same way but we can't store an INT model anymore right so what we're going to store is a pointer to a concept right in this case our concept is drawable object right so we want a concept for a drawable object so step 4 draw object it's just going to be a base class for our implementation here okay so we've got a base class which is our concept it's got a defaulted destructor and our string model and our int model inherit from it okay we're going to virtualize draw on it so there's our virtual draw function okay and we're going to provide a base way to draw a string okay so fairly simple set of transformations what our codes getting a little messy in a little while oh we also have to worry about copy right so before we could just copy our int model now we have to virtualize copy okay so we'll go down here to our concept and we'll add a virtual copy and we will implement virtual copy for a string model and our int'l but now we can do this we can put a string in here and this will work so we've got a little polymorphism but we've got a pretty big class at this point right but we didn't allow our polymorphism to complicate the client code and we're working with integers and strings which don't inherit from anything so that's pretty good so far the polymorphism is an implementation detail and that's where polymorphism should be okay but we have a lot of redundant code here right so what can we do with redundant code like this right we've got to draw functions that are almost identical right do we have a language facility to just write that once anybody the template right so we're going to template eyes our draw and we're going to sanitize our construction okay now down here we have to model implementations almost identical one for Strings and one for integers right well that could be a template just a model okay so now look what I can do I can write my own class and I can provide a draw function I could also just make my class serialized to to a standard stream and it would pick up my default draw function okay so I can provide a custom draw function or I can just provide a stream out for it and now I can store my class T inside of my documents okay my class T inherits from nothing so whatever I want it to be okay it's called the runtime concept idiom it allows polymorphism when is needed without inheritance the client is important with interfaces factories or class delegations okay so I can adapt anybody's third party code all I have to do is is is provide a draw method a draw function standalone function and I can throw it in and use it with this type okay in fact since I started this whole thing my document which is a vector of my objects itself I have a draw function because I'm calling draw a document see out right right there's last line we've been calling that all along so I can put my document into my document okay and that's going to work right and it's not going to explode because it recurs forever it puts the document into the document at the state the document within at the time that I copied it into the document okay so now I can have a document that contains 0 hello another document containing 0 on hello into that document my class key and of the second document ok so you want to shift polymorphic use you want to make sure we're using regular semantics for all of our operations that way everything works with containers promote interoperability and there's no prevent performance penalty this code is as efficient as the base class mechanism it is that's exactly what it is under the hood ok so but there is potential performance wins because I don't have to wrap integers into objects okay or strings into objects or third-party library things into objects and lug those things around my system now let me flip to a demo here you - play is Photoshop for anybody who hasn't seen Photoshop or hasn't seen Photoshop in a while I'm actually back on the Photoshop team when I first joined Adobe like twenty two years ago I worked on Photoshop for a number of years and now I'm working on a Photoshop again so this is a shot that I took of the at the beach in California some people think the seagull looks like it only has one leg there so let's fix that there we go so now the seagull doesn't look like it has one leg anymore let's do something horrible with this image right I wanted if we were sky let's paint in some lower sky my favorite color [Music] someone checked there okay so why am I doing this it's certainly not to display my great artistic talent but over here I have open what's called the Photoshop history panel okay and even if I went and did something like said take this entire document and say let's just revert it all the way back to where it was okay my history panel is still there and what does that mean that means that I can back up through every step of this document all the way back to when I opened it okay so up through Photoshop for Photoshop hat undo but it only had one level of undo okay now during Photoshop we said we want multiple level undo but we had a problem and that Photoshop was written using something called Mac app which is a framework that Apple used to ship and had these command objects and the way it would work is a command object would implement a bunch of member functions for do it undo it redo it commit it was the basic model so you would write a command you would implement those things and then you would get undo well the problem was over the initial Photoshop development is is people were storing the data that they needed in order to to undo redo okay prior to commit hanging out in fields of the document right well where would I put this I would put that there so if you did two commands back-to-back the second command might clobber the undo history for the first command okay and this was kind of strewn throughout the code right because you had to put the data someplace in order to implement redo so where do you do it you hang it off some member of the of the document and this was a problem the code base even at that time was big enough where it was like how in the world do we even find all of these places and one of my colleagues mark Hamburg said I have this great idea let's get rid of that model entirely all we're going to have is do it just do it and between each do it we're just going to make a copy of the entire document everything okay no Photoshop can handle massive documents right like you know we've worked with JPL to create documents that are the surface of Mars to a one meter resolution like the entire surface of Mars to a one meter resolution as one Photoshop image okay okay and our model is just make a copy make a copy okay so that gives you undo it gives you the ability to step in and out okay so but it also gives you more because I have this handy tool where is my tool this right here called my history brush so I can come along here and say well I kind of liked a little more of the blue there so I will anchor my history brush there and come down here and say well I'll just put back a little bit of that blue now I'm not you see I'm not painting blue I'm painting in the state document within when I put the blue there okay and if I say well no I want a little less blue I can paint back in the document in its original state maybe I didn't want the blue over the sequel's head okay so this feature came along for free because Photoshop already had a clone tool that could clone from one document to another document and it could clone from one point within a document to another point within a document and so if we have a whole stack of documents right that's all our history is is a whole stack of documents that I can clone from one document into another document so what does this have to do with our talk well let's take a look let me turn off bring your rain ok so this is what Photoshop does right right when we make a copy of the document it's a very lightweight copy right because most of the structure is actually shared okay so when we lay down a pink stroke the only thing that's new is the titles of the image with the paint stroke right Photoshop busts the entire image into a whole bunch of relatively small tiles and that lets us just modify portions of it so let's write an undo here okay so what we want is our history which is a vector of documents and we're going to add the following commands commit undo and current which just returns the top of that vector okay so all commit does is does a push back of our document state push back to back that's actually legal C++ and undo where we can do a pop back so we're going to write that now let's go all the way back up here we don't need to document our C tours but we're going to look at copies and we're going to take this code and we're going to change it so we create a history state and we do write which is we take our current document and we put in 0 in hello then we draw our current document we'll draw a line and then we'll commit that change into our history and then we're going to take the now current document we're going to put 42 World put the document into itself and put in my class draw it put a line and then we're going to undo which I'll be back to our commit state and draw the document one more time okay so that's what this is going to print construct our document copy copy copy okay and then we're going to display our document like that and then we're going to put it back to the state it was in initially okay so each of those copies come in because of course we're copying our document to put it back onto itself and that's going to copy all the objects within the document and if you haven't figured this out yet I don't like copies so what are we going to do well this is where all of our copies are coming from they're coming from a copy and assignment operator and I don't like that so get rid of it okay which means we don't need to virtualize it so get rid of that but we do have to make a change here right right now our object would just be movable with a unique pointer so let's put a shared pointer in there okay but at the start of this talk I told you about how evil shared pointers were so let's fix that the shared pointer to a Const no place in this piece of code do we actually modify this object the internals of it right and an observation is that for polymorphic use cases of objects having mutable objects is the extreme extreme exception where there is benefit for that and there's a good reason why so if I have a virtual function that's mutating my object okay then what I am doing one of two things I'm either saying that my subclasses can provide a different implementation okay which that's very problematic because that's going to change the performance guarantees of those of that operation and potentially a bunch of other operations on my object or I'm saying that my subclasses want to observe when this thing is set but they're not actually mutating it so rather than having them override by my mutating virtual functions what I should do is provide an observer function where I will call them when I have changed things okay so what we're going to do is make shared like that and now all of our copies okay and the code still works and the code still has value semantics okay the side benefit here if I wanted to come along and say in the middle of some transaction the user hits command S and I save this document I can just peel off a copy of my document right I've got document there in my ace and lambda bound to my current age let's just make a copy of this document so I can peel off the copy of this document throw it into a thread and draw it from the thread right and if you say I had a sweep for three seconds there that's just so it didn't come over and that's what this would print okay which is the state of the document at the time that I should save it okay there's no raise conditions there I'm good and clean so this is thread safe as well okay so compared to an inheritance based design is more flexible it's not intrusive it's more efficient polymorphism is only paid for when needed so which is a huge benefit so I find if you if I look at most class hierarchies people don't know they just like making everything virtual right right right they're like they're like well I needed or I needed this to be virtual in this one case so it's virtual everywhere okay it turns out that if you write code this way that you'll find that you need actual polymorphic behavior in just a few set of cases right instead of all over in your code base so less error-prone the clients don't have to worry about heap allocation they don't have to worry about object lifetimes or ownership it's exception safe and it's thread safe so we created think about what we did here we created a little library where we have an object type that holds any drawable object where drawable is defined as has a stream out operator or its own draw function okay a document of those history state with undo and commit and we did that in less than two slides worth of code okay so as we increasingly move to heavily threaded systems using futures and reactive programming and task queues value semantics becomes critical to avoid locking and to allow us to reason about code right if you look in my safe I didn't have any mutexes I didn't have to lock my document just peel off the copy and send it away I hope the languages and libraries will evolve to make creating polymorphic types in this format easier if you look at like language go has this built in the language Swift has this built in and I'll take some credit for for influencing Swift I gave this talk the first time over a decade ago at boost con which was Dave Abrams conference which is now C++ now it was a keynote there and Dave Abrams is one of the key designers on the Swiss language and so go watch his talk from WWDC and you'll see it's very much a copy of this talk just cast into Swift right some thanks there - Aleks dip not Howard Hannon and Dave Abrams who've all provided input into this a link there to where you'll find my papers and presentations talks from this conference of today's version of this talk isn't yet up but it will be before this conference is over and I cited a Alex and Paul's book elements of programming a few times in this talk so that's the actual reference for that book so it's a good book a difficult read if you haven't read it before so any questions with that what are the drawbacks to this approach so the yeah so the drawbacks it's a little difficult with this approach if you have the equivalent of of multi inheritance where you have a polymorphic use case where you want to handle things that are kind of of a variance they either satisfy concept a or concept to be it can be difficult to encode things this way there are some ways around it but they tend to get silver boasts that I usually say if you are really in that situation probably multiple inheritances is the way to go another drawback because this is non-intrusive also means that it's not self documenting right so when I have my class type that doesn't inherit from anything but yet in order to use it correctly it has to satisfy the requirements of drawable all right so it's just got the standalone draw function there's nothing in the code that tells you hey the reason why that function is to err is so that this can interoperate in this other place right so there's a there's a pro/con there the plus side is I can just lift up my drawable object and drop it into another system that has different polymorphic requirements and I don't have to carry some whole class hierarchy along with it that's just extra baggage trade-off [Music] other than that I don't don't don't really know of any so other than it can be a little verbose to type like I said I'd like to see a little more reflection in the language to get rid of the boilerplate we the conflict so the question there is I have my draw if a library implements their own draw will it conflict potentially this is one of the downsides of using a dl-44 name lookup which is argument dependent lookup which we want to use in this case because we want to call draw and pick up the draw that they implement so the idea there is is choose your names carefully and know that their hook point and sometimes that might mean manually prefixing your names like you know my class underscore draw whatever or my library underscore draw so that you're clear that I mean draw with this semantics if it's a common name that's probably a good thing to do so so that leads into a whole discussion of why I think you know ADL was the wrong solution to the problem that tries to solve and look at something any other questions any questions outside the talk okay thanks you're the rest of the conference
Info
Channel: NDC Conferences
Views: 51,840
Rating: 4.9266057 out of 5
Keywords: c++, sean parent, ndc, ndc london
Id: QGcVXgEVMJg
Channel Id: undefined
Length: 57min 33sec (3453 seconds)
Published: Mon Feb 27 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.