# Crash Course: poly # Table of Contents * [Introduction](#introduction) * [Other libraries](#other-libraries) * [Concept and implementation](#concept-and-implementation) * [Static polymorphism in the wild](#static-polymorphism-in-the-wild) # Introduction Static polymorphism is a very powerful tool in C++, albeit sometimes cumbersome to obtain.
This module aims to make it simple and easy to use. The library allows to define _concepts_ as interfaces to fullfill with concrete classes withouth having to inherit from a common base.
This is, among others, one of the advantages of static polymorphism in general and of a generic wrapper like that offered by the `poly` class template in particular.
What users get is an object that can be passed around as such and not through a reference or a pointer, as happens when it comes to working with dynamic polymorphism. Since the `poly` class template makes use of `entt::any` internally, it supports most of its features. Among the most important, the possibility to create aliases to existing objects and therefore not managed directly. This allows users to exploit the static polymorphism while maintaining ownership of their objects.
Likewise, the `poly` class template also benefits from the small buffer optimization offered by the `entt::any` class and therefore minimizes the number of allocations, avoiding them altogether where possible. ## Other libraries There are some very interesting libraries regarding static polymorphism.
Among all, the two that I prefer are: * [`dyno`](https://github.com/ldionne/dyno): runtime polymorphism done right. * [`Poly`](https://github.com/facebook/folly/blob/master/folly/docs/Poly.md): a class template that makes it easy to define a type-erasing polymorphic object wrapper. The former is admittedly an experimental library, with many interesting ideas. I've some doubts about the usefulness of some features in real world projects, but perhaps my ignorance comes into play here. In my opinion, its only flaw is the API which I find slightly more cumbersome than other solutions.
The latter was undoubtedly a source of inspiration for this module, although I opted for different choices in the implementation of both the final API and some features. Either way, the authors are gurus of the C++ community, people I only have to learn from. # Concept and implementation The first thing to do to create a _type-erasing polymorphic object wrapper_ (to use the terminology introduced by Eric Niebler) is to define a _concept_ that types will have to adhere to.
In `EnTT`, this translates into the definition of a template class as follows: ```cpp template struct Drawable: Base { void draw() { this->template invoke<0>(*this); } }; ``` The example is purposely minimal but the functions can receive values and return arguments. The former will be returned by the call to `invoke`, the latter must be passed to the same function after the reference to `this` instead.
As for `invoke`, this is a name that is injected into the _concept_ through `Base`, from which one must necessarily inherit. Since it's also a dependent name, the `this-> template` form is unfortunately necessary due to the rules of the language. However, there exists also an alternative that goes through an external call: ```cpp template struct Drawable: Base { void draw() { entt::poly_call<0>(*this); } }; ``` Once the _concept_ is defined, users need to specialize a template variable to tell the system how any type can satisfy its requirements: ```cpp template inline constexpr auto entt::poly_impl = entt::value_list<&Type::draw>{}; ``` In this case, it's stated that the `draw` method of a generic type will be enough to satisfy the requirements of the `Drawable` concept.
The `poly_impl` variable template can be specialized in a generic way as in the example above, or for a specific type where this satisfies the requirements differently. Moreover, it's easy to specialize it for families of types: ```cpp template inline constexpr auto entt::poly_impl> = entt::value_list<&std::vector::size>{}; ``` Finally, an implementation doesn't have to consist of just member functions. Free functions are an alternative to fill any gaps in the interface of a type: ```cpp template void print(Type &self) { self.print(); } template inline constexpr auto entt::poly_impl = entt::value_list<&print>{}; ``` Refer to the variable template definition for more details. # Static polymorphism in the wild Once the _concept_ and implementation have been introduced, it will be possible to use the `poly` class template to contain instances that meet the requirements: ```cpp using drawable = entt::poly; struct circle { void draw() { /* ... */ } }; struct square { void draw() { /* ... */ } }; // ... drawable d{circle{}}; d.draw(); d = square{}; d.draw(); ``` The `poly` class template offers a wide range of constructors, from the default one (which will return an uninitialized `poly` object) to the copy and move constructor, as well as the ability to create objects in-place.
Among others, there is a constructor that allows users to wrap unmanaged objects in a `poly` instance: ```cpp circle c; drawable d{std::ref(c)}; ``` In this case, although the interface of the `poly` object doesn't change, it won't construct any element or take care of destroying the referenced object.