 Welcome, my name is Si, I'm Microsoft C++ Developer Advocate and I'm going to be talking about dynamic polymorphism with code injection and metaclasses. For those who aren't familiar with this term, some definitions, polymorphism itself is the provision of a single interface for entities of different types, so you have one interface you program to and that will give you different behaviour depending on what the types are. The two types we're kind of concerned with today are dynamic polymorphism and static polymorphism. Dynamic polymorphism is at runtime and static is at compile time. Dynamic polymorphism gives you different behaviour based on your dynamic type and static of the compile time type, the static type. For dynamic polymorphism, the main way we do that in C++ is inheritance and static polymorphism is usually done with overloading or with templates. Now, a few years ago, Louis Dion gave a really fantastic talk about dynamic or runtime polymorphism at C++ now and a lot of the ideas there are kind of the basis for some of the parts of this talk, so let's see what he has to say about runtime polymorphism. Okay, so he says to listen to Sean Parent instead of him, so let's go back to Sean Parent. And a few years prior to that even, I think this is 2012, Sean Parent did a great talk called inheritance as the base class of evil and he's done many similar talks since then. The idea behind this talk and Louis talk is that there's a lot of problems with inheritance as a facility for doing dynamic polymorphism in C++ and there are ways which we can try and alleviate some of these and this is what I'm going to be doing in this talk, implementing better solutions. So there's some of the problems with inheritance, I would recommend going back and watching these talks if you want more details, but as a kind of simple description. One of the main problems with inheritance is that you often get dynamic allocation where you might not need it. So if we have some kind of hierarchy like this, we have base class and A and B which derive, we can't have like factory function which returns base by value. We can't store bases in a vector unless we are fine with just slicing off the derived part of an object. This is called slicing if you return a polymorphic object by value, it's just going to get rid of the polymorphic part and you're left with your base part. It's usually not what you want so we end up doing something like this and returning unique pointers or storing unique pointers or maybe we can get away with storing references if we know the lifetimes here. This means that we're doing dynamic allocation, we can't usually do like a small buffer optimization or store these things in place without doing a load of extra work, which you know it's possible to do but with the kind of basic fundamentals that virtual functions and inheritance give you, it's not right there. As I kind of alluded to, you also then get the problem of ownership and also nullability, usually we're returning unique pointers or shared pointers or something we don't like throwing around owning raw pointers and sometimes we can decorate these and things like that but it's something we have to think about. We have to think about the ownership of this resource and we also might need to think about nullability, sure we're getting a unique pointer and we know what the ownership of that is but can it be nulled, we have to check for this. These are all concerns which we have to think about when we use inheritance. Another problem is that they're intrusive and that they require you to modify the child classes if you want to make the most of inheritance, well if you want to use inheritance even at all. So say I have a base class in my library and some other library has another class which corresponds to the correct interface, the base class has a do thing virtual function and otherlibx has a do thing function, they have the same signature. We might want to be able to use x as a base but we can't do that. This line will not compile because otherlibx does not inherit from mylib base and maybe we can just change otherlibx, maybe we can't, maybe it's code we don't own. So then we have to go all the way and write wrapper classes and things like that which again is possible but it's more code, it's more work, more things to maintain, more things to write, more things to get wrong. Another issue is that now that we have pointers everywhere we don't have value semantics, we can't copy a base class object without getting rid of the polymorphic stuff unless we then implement something else on top like a virtual clone interface, again something you have to build on top of inheritance yourself. And also we have changes in semantics for algorithms and containers, you want to store a polymorphic object in a stud set for instance, you'd have to provide your own custom comparator object to make sure that you're actually comparing the underlying objects and not just the pointers, unless the pointers are what you want. So those are some of the problems with inheritance and throughout this talk I'm going to come back to this list and we're going to try and solve all of these problems and we're going to solve it by essentially implementing inheritance and virtual functions from scratch by hand. The technique we're going to use is usually called type erasure and if you look at Sean parent stocks or Louis stocks they have a lot of detail about that as well. This is the hierarchy we're going to be trying to implement by hand. We have an animal class which has a virtual destructor and one virtual member function called speak. Then we have cat and dog which override speak, fairly simple. When you actually want call a virtual function on an object because the call has to be resolved at runtime, not compile time, we need to store some information in every polymorphic object. So if we have an object called Felix which is a cat, then this is going to store a pointer to a V table, a virtual table. The V table is essentially a description of how to call virtual functions for a given polymorphic object and this V table is now going to point to the speak function in cat. So in order to call speak we need to grab the V table from our polymorphic object, then grab the member function from the V table and then call it. So there's a few levels of indirection. Now if we're wanting to implement all this by hand, we need to essentially write our own V table and do all of this in direction manually and hopefully by doing this and playing around a bit we can get a better solution. What I kind of want this to look like by the end is this. So we have our animal class and that's going to have some magic which I'm going to gloss over for now. And then our cat and dog objects, cat and dog types are going to have member functions. No, these are not virtual functions. There's no inheritance but with the magic which is in the animal type, we're going to end up with a use like this. We can just create an animal from a cat. We can create an animal from a dog. We can call speak and it will call the right function, right? So this is how we're going to achieve this. Next we need to say what the V table layout is going to be. So for our animal type, we're going to have one speak member function. So we need to say a V table for an animal looks like this. Then we have to define what a V table implementation is for a given concrete type like cat or dog. And then when we construct an animal object, we're going to capture the V table pointer which we need for a cat or a dog or for something else. And then when we call speak or some other member function, we're going to forward those calls through the V table and you'll see what all of this looks like in a moment. Again this is the interface which we're trying to emulate and we're going to start by declaring our V table. To start with we're just going to have two function pointers. So our V table has one function pointer for speak and one for destroy which is going to call a destructor and do any other cleanup we need. These are both taking void pointers because we're going to be passing in the actual concrete object which we're storing and these things are going to be casting internally. This is all we need for our V table layout. This just says this is what the V table looks like for an animal. Now we need to fill in these virtual function pointers for our given concrete types. We can do this with a template. This is a C++ 14 variable template even. So what we need here is to store a function which we'll call speak on the correct type and a function which will destroy the object and also call delete because we're going to be managing our own memory inside animal. We can use lambdas for this. It looks kind of ugly but it's not that bad. So these are two functions. The first one is just going to cast to the correct type and then call speak and then the second one is going to cast and call delete. Leave this up for just a second so you can have a look. Again this is just a normal variable template. We are capturing the type, this concrete type and generating functions which are going to cast to the correct thing internally. Now that we've defined the V table we need to capture the V table pointer for the correct type on construction. So if we have our animal type and we're going to store our pointer to the concrete object and we're going to store a pointer to the V table then when we construct an animal object we're going to make the constructor a template and take any type. You could use a static assert or SVNA here if you wanted and you could also use perfect forwarding but this is, you know, slide where. When we call the constructor we're going to allocate a new copy of whatever we pass in and store that in our concrete object and then we're in our concrete pointer and then we're going to capture the V table for the given type. I'll leave this up for another minute. So you see the V table for there is going to be the variable template which we defined just here. So we're capturing the pointer for this V table. Okay, now that we've done that we just need to forward our calls on through the V table and that looks like this. Whenever we call speak we indirect through our V table pointer and call speak with the pointer to our concrete object and remember this is going to cast to the right type and call the right function. So it's kind of, we're going through a few levels of indirection but we've mostly got to where we want. Now we can have our cat type and our dog type both with speak functions and then we can construct our animals and call speak and this will allocate copies of the cat and dog objects, capture the V table pointers and forward the calls through. Okay, so these were our problems we had with inheritance and we've solved some of these problems now. Now that we're, we don't have pointers external to our interface, we're handling the memory allocation internally then we don't have our ownership and nullability considerations anymore. It's just an object, it can't be null. We know who owns it, it's just managing its own memory. We've also got rid of the problem of intrusiveness. We don't need to modify the child class anymore. We don't need to decorate it with saying that inheritance from the base class it will all just work but we still have these three other problems. We don't have value semantics because we didn't define what it means to copy this thing. We can't store it in a container for the same object and we're always doing dynamic allocation whenever you create an animal object but we can solve some of these. If we want to do value semantics then we just need to extend our V table a little bit. We're going to have an additional clone and move clone and these do mostly what you think they will just call new and either copy or move depending on which one it is. I'll just flash up the implementations, they're kind of messy but it's pretty much what you would expect and then inside our animal class when we copy or move we're going to call the clone or move clone functions in the V table and that will allocate a copy or allocate a move and we're going to use the V table from the object which we're being created with and then you would do the same thing with the copy assignment and move assignment operators and then we get our value semantics back. We can create an animal, we can copy into it, we can assign. It's pretty much what you would expect from a normal value like interface but we're using polymorphic objects and it's allocating under the covers. We can also now have a container and we can iterate over it and we can store cats and dogs and everything else with the same interface and it all just works. So coming back to our list, we've got our value semantics back and we've also got back the ability to store in containers and use algorithms normally assuming that we define the necessary comparators and things like that in our animal type. So we're only left with one problem. We're not going to solve this for a little while yet but I want to flash up a little issue with the current implementation and that's that while it's giving us a lot of functionality and it's quite usable, the amount of code we have to write is this. Don't try and read it, this is just all the code I've showed you already but it's just to show you that to achieve this, we've had to write a bunch of boilerplatey code and sure you could get rid of some of this with macros or templates or more complex solutions but it's not simple to implement. So the rest of the talk is going to be how do we make this easier and also solve that last problem of dynamic allocation and the first thing we need in order to do that is static reflection. So reflection is the ability of a program to introspect its own structure. So a program being able to ask questions about itself and then use that information in order to achieve, to carry out some action. Static reflection therefore is the ability to do that at compile time and we already have some facilities for doing this in C++, mostly out of the type traits header. So we have things like is array which tells you if some type is an array and we have is same which tells you if two types are the same. But there's a few things we can't do. We can't take an enumerator and give you the name of it. We can't iterate over the members of a type and that's pretty much necessary for what we're trying to do. Fortunately there is a paper called scalable reflection in C++ which is being discussed in the committee. Reflection is being discussed for a very long time but it seems like this is the direction which we're going to be going in and this is what the interface looks like. So if we take that example of generating the string name from an enumerator value, we're going to take in some enumerator and then we're going to loop over all the members of the enum. So to break this down a little bit, the first thing is this reflector. This is reflecting on the type T. It generates an object which describes the structure of T. And based on this object we can then use this members of to get a range which represents information about the members of that type. So for an enum that will be all of the different enumerator values. So for every one of those enumerator values, we can check if our value is that. This X bar ID takes some reflection and turns it into an expression. So if we had a colour enum and red, yellow, green then using this X bar ID on the information for yellow would turn it into yellow, the expression. So we're testing against the value and then we're going to return the name of that enumerator if there was a match. And if we didn't find one, we'll return a named or some default value. So you can see that this is asking things about the structure of a program at compile time and then doing something based on that. But this isn't quite enough for what we want to do because this is just getting information and then exposing that information to the rest of the program. We want to generate code based on the information we read and we can't just use templates for it because we're taking in names, we're going to be generating code from names and templates don't have facilities for that. So what we need is code injection. As an example, so we have this class point which has two member data and X and Y and we want to write getter functions for it. I'm not saying you should use getter functions. This is just an example because people understand what getter functions are and you'll notice that this is a pretty mechanical transformation. We have our X and Y member variables and our member functions are returning those and their name is just prefixed with get underscore. So if we had the right facilities, we could generate this code programmatically and that's what code injection allows us to do, would look something like this. This const eval block just says run this part of the program at compile time and this generate getters function I'm going to show you in a second. We're passing in the reflection of point and this is going to inject our getter functions. Generate getters will look a bit like this. It's a function which takes this meta info that's the type which reflects per generator. It's a representation of the type or a member or something like that. Any kind of entity which we can represent, this represents its reflection and we're looping over all of the members and if the member is a non-static data member, then we're going to generate a getter for it. Hopefully this is fairly clear. This non-static data member is like a compile time predicate on this reflection information and then generate getter is where the actual magic happens. Some new syntax here and I want to preface this with this is not final syntax. This underscore underscore fragment thing looks ugly and it will not be what we end up with in C++ I really hope. This arrow starts an injection statement and we're injecting a class fragment. There's a few different kinds of fragments. There's class fragments which can be injected into a class. There's namespace fragments which can be injected into a namespace and block fragments which can be injected into a function or something. We have struct after the fragment so that says this is a class fragment. The injection statement is going to inject some code at the current execution point where the current execution point in this case is this generate getter's function so inside this const eval block that's where the code we generate is going to go. Going back to the code so we want to generate something which looks like this. It won't be exactly this because we want to generate it based on the information we get in but say we're given the reflection for the x data member. This is what we would want to generate. So instead of int instead of hard coding int we're going to get that information from the reflection. We're going to use the type name of the member's type. So since x was a type int this will kind of collapse down to int. Then similarly we don't want to hard code in get x. We want to take the name of the member and we want to prefix it with get underscore. We do this with unqual ID. This type name unqual ID, x bar ID, these are all called rayifiers. They take some reflection information and turn it back into something in the kind of C++ code world. So unqual ID just takes some reflection and turns it into an identifier and we can prefix it or suffix it or whatever. And then inside this member function we're going to return the member as an expression. Leave that for just a second. So now when we call our generate getters function it's going to generate code which looks like this. So now we know how code injection works or at least the basics of it. It'll work out how we apply that to our current problem of doing dynamic polymorphism. Right now coming back to our animal class, we want to generalize this. We don't want this to, we don't want to hard code everything by hand. We want to generate the V table. We want to generate the V table implementations, everything like that automatically. So we're going to take this animal class and we're going to turn it into a template. I'm going to call it type class 4 because this is pretty similar to Haskell's type classes or Rust's traits. It's a similar kind of idea. So instead of our animal class we have our type class 4 template. The only difference here is we now have a template parameter and our V table is parameterized by this facade. So what I'm calling a facade is essentially just a description of an interface. So for animal it would look like this. This will never have any implementation, the speak function won't have an implementation. It won't have any constructors or data members. It would just be a description of the member functions which you can call. And then if we wanted our animal class like before, we would have something like this. Our animal type is just the type class for our animal facade. And of course you could have any kind of facade you want. You could have something with multiple member functions with parameters, different return types and it should all just work. We shouldn't have to write any code to generate things by hand. Now the algorithm we need to go through for doing our code injected virtual functions is exactly the same as before. We're declaring what a V table layout looks like. We're filling in those function pointers, we're capturing the V table pointers and we're forwarding calls. So getting started on the V table. This was our V table before. We had our speak function destroy clone move clone. Now those last three are going to be the same for any kind of facade. The only thing which we need to generate from the facade is the speak function, but with speak function pointer. So we're going to make V table into a template. And now we need to generate this speak function pointer type along with any other function pointers which the facade has based on reflection information. So we're going to start with the const eval block, which again just says to evaluate this compile time. And now is where the magic happens. Don't worry too much about this for each declared function is just a nice helper which I wrote where you give it a reflection and a lambda and it's going to call that lambda with every member function declared in the facade and it will give you the function reflection, the return type and any parameter information. You can see the implementation of it. I've got a link at the end, but it's not it's not that interesting. And then inside this lambda for every declared function, we're going to inject a member function pointer type. And again, this is what we want to get out at the end for our speak function. And we're going to I'm going to transform it so you can see what it transforms into. So again, instead of hard coding our avoid type, we're going to use the type, the name of the return type instead of hard coding our speak identifier. We're going to get the name of the function from the facade. And if that member function took any additional parameters, then we're going to inject them like this. And this will end up with, you know, void speak pointer and then any parameters. After we've done that, we need to define what those V tables look like, what the V table function pointers look like for a concrete type. So if we declare an instance of our table, then our speak function is going to look like this. This is essentially the same thing as before, but I've expanded it out a little bit. So we want to generate this table.speak from our facade. So we're going to inject a fragment. This is a block fragment. It doesn't have any structure class or namespace after the fragment identifier. And similarly, we're not going to use speak. We're going to use the name of the function and we're going to inject our parameters and use those identifiers as arguments. And then we're also going to return this function call in case the function we're calling returns anything. I want it a little bit faster there because hopefully you're seeing that you take the kind of algorithm you use to do this is you take what you want to end up with and you slowly transform it into ray of fires based on reflection information. This is how you write code injection code. After we've defined our V table, then we're going to catch the V table pointers. And this actually looks exactly the same as before. We have a constructor template and we're going to allocate a copy and capture the V table pointer. The last thing is forwarding calls. The code for this is kind of ugly and is mostly similar things. So I'm not going to step through it, but I'm going to flash it up for completeness sake. This is, again, just going to forward calls through the V table so that we end up calling the correct function. And now, instead of writing all of this code for writing our animal class, all we need to do is declare what our facade looks like. And then alias are this type class four to the class we want. This is a huge cut down in code. I've got a cat here meowing at me. Okay, so we can actually do better than this though. We can we do better than this. We can instead of just taking our facade as a template parameter and always dynamically allocating, we could take the storage policy as a template parameter. So now, instead of just allocating all the time, we could use a small buffer optimization or always use in place storage or or anything we want, we can make it configurable. Similarly, we can take the V table policy as a parameter. And this allows us to configure the storage of the V table. So now if we want to to tune things a bit, we can say that, you know, we could just use the type class for an animal facade. And that might be the same thing as using remote storage and remote V table, which means we always allocate the concrete type, the concrete object. And we always just store the V table in some static memory and store pointer to it. But if we want to do something different, then we could use a small buffer optimization with 32 bytes and always use and store all of the V table internally to the object so that we're not having to do two jumps to get to a member function. We just interact once. If we wanted to get really fancy, we could even say to always store the the object in place and we'd get a compiler error if if it doesn't fit. And then we want to split the V table. So if we have some functions which are called a lot more often than others, then we could store the common ones inside the object and we could store the ones which are not called as much externally. And you could do anything you want. You could make this as complex or simple as you want. And this actually gets rid of the the requiring dynamic allocation problem because we can now use small buffer optimizations and in place storage. If we know more about our types, we don't have to just always allocate. And now we've solved all of those problems with inheritance. But we can actually go even further. We could instead of just telling to, you know, we're declaring a facade and then we're aliasing. And if we use meta classes, then it could look like this. And this is really, really powerful. I mean, it's it's just a small difference from here. But it's essentially saying that we are now building our type class as a real abstraction as a library, which we can now use from our code. So I'll talk a little bit about meta classes now. So meta classes are a proposed extension to C++. And here's a description from the paper. They let programmers write a new kind of efficient abstraction, a user defined named subset of classes that share characteristics like user defined rules, defaults, and generated functions, where generated functions is what we're using in our example. Coming back to our point class, you know, this just has X and Y members. But we can't compare it. We can't do orderings on it. It would be nice if we could just, you know, say that I want to do lexicographical comparisons on this. I want to be able to treat this just like a value. And you could do this with something like a value meta class. And what this would do is inject all of the necessary code into the point class. This is a meta class definition. It's just a const eval function, which takes some reflection information and does some injections. Now, you could go and read the whole meta classes paper. And you should. It's got a lot of great examples and things like that. But now that we've introduced reflection and code injection, we can actually define it on one slide. If we have a point class which uses the value meta class, then this is the same as just taking the implementation of point, copying that into some hidden namespace and some unnamed type, and then defining our point class by calling the value meta class on its reflection. I'll leave this up because it's a little bit weird to get your head around. But this is the entirety of the meta classes proposal in one slide, mostly. It's really quite powerful. And like I said earlier, it's a small change from our code injection version. But it's really making us able to write our own abstractions and embed them right into the language. So we had our type class meta class. And that would be the same kind of thing. It would be a compile time function which takes some reflection. And it would inject all of the similar code which we had for our type class for. And then all we need is class type class animal or any name you want. And then any number of member function declarations. And now we can create a vector. We can store anything which corresponds to this interface in this vector. We have full value semantics. We are only dynamically allocating if we don't care or haven't tuned it yet or don't know anything about the sizes of our objects. So this is really powerful and concise, understandable, maintainable, solves a lot of problems with inheritance. But it does have some, there are some concerns about this as well. One concern is runtime performance. I ran some, did some tests. This is all available on Compiler Explorer. There's an experimental compiler. So I did a few tests. And sometimes everything can get inlined. The compiler sees through everything. Not all the time though. So if you're doing something a little bit more complicated, you might actually get better performance from virtual functions on some compilers. Because especially compilers like GCC have been doing a huge amount of work on de-virtualization. And that really shows sometimes. I think in the future this would get better. If this kind of paradigm caught on, compilers would get better at optimizing through function pointers rather than relying on de-virtualization. So again, it's that kind of thing, benchmark, talk to your compiler vendors, file bugs if there's things which should be optimized but aren't. Compiletime performance is a concern. And it's not something I want to benchmark right now because the experimental compiler is all just trying to have a proof of concept and is not optimized for compile time yet. I don't even want to give you a ballpark figure on that. But when as we spend more time on meta classes, code injection, compile time performance is something we should be thinking about. Especially for industries which need very, very fast iteration times like video games. We can't be affording to murder compile time performance in order to get things which we could build by hand in a reasonable amount of time. So yeah, this is something which we need to be keeping in mind a lot as we move forward. I don't want to lose out on compile time performance just for the niceties which we have here. A more kind of philosophical concern which I have is that this is essentially a different and incompatible way of specifying concepts. You know, our facade was essentially a concept. It was saying we have some interface and anything can fulfill this interface and we're gonna be matching these things at compile time. And it's essentially just concepts but instead of working on usage patterns we're working on signatures. So this could end up with a bifurcation where maybe you want to say you want to have a concept which works at compile time and at runtime. You want to be able to say I have a template and I take anything which fulfills this interface but then you also want to say, oh, I want to store a vector of these objects. Right now you would have to specify the compile time concept and the runtime concept in different ways and that feels really wrong. There are some papers on virtual concepts. There's maybe some people looking at how we could do reflection of our concepts but I don't know where that's gonna go and I'm not sure what the best approach there is. So I'd love to hear more thoughts on this point. As far as future work goes, there is a paper called PFA which really lays down a lot of these concepts and has a proposed interface for exactly what I'm showing here. I'm actually working on an implementation of this paper based on the Metclasses compiler at the moment. Here's some links. All of the code for this talk is on compiler explorer. You can grab it, play about, see what the compiler's output. I have a kind of different style prototype implementation which is on GitHub. It uses a different way of implementing things and it's maybe a bit better commented than the one on compiler explorer but have a look at both, see what the different approaches are. The experimental compiler is developed by lock three. Andrew Sutton and Wyatt Childers are the main devs on that I think and they're doing a really good job as part of writing this and some other things. I've sent them bug after bug and they're always knocking them out. So please check that out. Send them bugs, send them PRs. I'm sure they would appreciate that. You can check out the compiler on compiler explorer at cpbx.gov. And here's a couple links to some of the papers which I described as well. And now we'll move on to questions. Thank you very much.