|
|
@@ -26,17 +26,16 @@ This module aims to make it simple and easy to use.
|
|
|
|
|
|
The library allows to define _concepts_ as interfaces to fulfill with concrete
|
|
|
classes without having to inherit from a common base.<br/>
|
|
|
-This is, among others, one of the advantages of static polymorphism in general
|
|
|
+Among others, this is one of the advantages of static polymorphism in general
|
|
|
and of a generic wrapper like that offered by the `poly` class template in
|
|
|
particular.<br/>
|
|
|
-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.
|
|
|
+The result is an object to pass around as such and not through a reference or a
|
|
|
+pointer, as it happens when it comes to working with dynamic polymorphism.
|
|
|
|
|
|
Since the `poly` class template makes use of `entt::any` internally, it also
|
|
|
-supports most of its feature. Among the most important, the possibility to
|
|
|
-create aliases to existing and thus unmanaged objects. This allows users to
|
|
|
-exploit the static polymorphism while maintaining ownership of objects.<br/>
|
|
|
+supports most of its feature. For example, the possibility to create aliases to
|
|
|
+existing and thus unmanaged objects. This allows users to exploit the static
|
|
|
+polymorphism while maintaining ownership of objects.<br/>
|
|
|
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.
|
|
|
@@ -44,7 +43,7 @@ of allocations, avoiding them altogether where possible.
|
|
|
## Other libraries
|
|
|
|
|
|
There are some very interesting libraries regarding static polymorphism.<br/>
|
|
|
-Among all, the two that I prefer are:
|
|
|
+The ones that I like more are:
|
|
|
|
|
|
* [`dyno`](https://github.com/ldionne/dyno): runtime polymorphism done right.
|
|
|
* [`Poly`](https://github.com/facebook/folly/blob/master/folly/docs/Poly.md):
|
|
|
@@ -69,18 +68,18 @@ use the terminology introduced by Eric Niebler) is to define a _concept_ that
|
|
|
types will have to adhere to.<br/>
|
|
|
For this purpose, the library offers a single class that supports both deduced
|
|
|
and fully defined interfaces. Although having interfaces deduced automatically
|
|
|
-is convenient and allows users to write less code in most cases, this has some
|
|
|
+is convenient and allows users to write less code in most cases, it has some
|
|
|
limitations and it's therefore useful to be able to get around the deduction by
|
|
|
providing a custom definition for the static virtual table.
|
|
|
|
|
|
-Once the interface is defined, it will be sufficient to provide a generic
|
|
|
-implementation to fulfill the concept.<br/>
|
|
|
+Once the interface is defined, a generic implementation is needed to fulfill the
|
|
|
+concept itself.<br/>
|
|
|
Also in this case, the library allows customizations based on types or families
|
|
|
of types, so as to be able to go beyond the generic case where necessary.
|
|
|
|
|
|
## Deduced interface
|
|
|
|
|
|
-This is how a concept with a deduced interface is introduced:
|
|
|
+This is how a concept with a deduced interface is defined:
|
|
|
|
|
|
```cpp
|
|
|
struct Drawable: entt::type_list<> {
|
|
|
@@ -108,12 +107,12 @@ struct Drawable: entt::type_list<> {
|
|
|
};
|
|
|
```
|
|
|
|
|
|
-In this case, all parameters must be passed to `invoke` after the reference to
|
|
|
+In this case, all parameters are passed to `invoke` after the reference to
|
|
|
`this` and the return value is whatever the internal call returns.<br/>
|
|
|
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
|
|
|
+the language. However, there also exists an alternative that goes through an
|
|
|
external call:
|
|
|
|
|
|
```cpp
|
|
|
@@ -165,12 +164,12 @@ struct Drawable: entt::type_list<bool(int) const> {
|
|
|
|
|
|
Why should a user fully define a concept if the function types are the same as
|
|
|
the deduced ones?<br>
|
|
|
-Because, in fact, this is exactly the limitation that can be worked around by
|
|
|
-manually defining the static virtual table.
|
|
|
+In fact, this is the limitation that can be worked around by manually defining
|
|
|
+the static virtual table.
|
|
|
|
|
|
When things are deduced, there is an implicit constraint.<br/>
|
|
|
If the concept exposes a member function called `draw` with function type
|
|
|
-`void()`, a concept can be satisfied:
|
|
|
+`void()`, a concept is satisfied:
|
|
|
|
|
|
* Either by a class that exposes a member function with the same name and the
|
|
|
same signature.
|
|
|
@@ -179,7 +178,7 @@ If the concept exposes a member function called `draw` with function type
|
|
|
interface itself.
|
|
|
|
|
|
In other words, it's not possible to make use of functions not belonging to the
|
|
|
-interface, even if they are present in the types that fulfill the concept.<br/>
|
|
|
+interface, even if they're part of the types that fulfill the concept.<br/>
|
|
|
Similarly, it's not possible to deduce a function in the static virtual table
|
|
|
with a function type different from that of the associated member function in
|
|
|
the interface itself.
|
|
|
@@ -200,8 +199,8 @@ struct Drawable: entt::type_list<> {
|
|
|
};
|
|
|
```
|
|
|
|
|
|
-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.<br/>
|
|
|
+In this case, it's stated that the `draw` method of a generic type is enough to
|
|
|
+satisfy the requirements of the `Drawable` concept.<br/>
|
|
|
Both member functions and free functions are supported to fulfill concepts:
|
|
|
|
|
|
```cpp
|
|
|
@@ -251,15 +250,15 @@ struct DrawableAndErasable: entt::type_list<> {
|
|
|
```
|
|
|
|
|
|
The static virtual table is empty and must remain so.<br/>
|
|
|
-On the other hand, `type` no longer inherits from `Base` and instead forwards
|
|
|
+On the other hand, `type` no longer inherits from `Base`. Instead, it forwards
|
|
|
its template parameter to the type exposed by the _base class_. Internally, the
|
|
|
-size of the static virtual table of the base class is used as an offset for the
|
|
|
-local indexes.<br/>
|
|
|
+_size_ of the static virtual table of the base class is used as an offset for
|
|
|
+the local indexes.<br/>
|
|
|
Finally, by means of the `value_list_cat_t` utility, the implementation consists
|
|
|
in appending the new functions to the previous list.
|
|
|
|
|
|
-As for a defined concept instead, also the list of types must be extended, in a
|
|
|
-similar way to what is shown for the implementation of the above concept.<br/>
|
|
|
+As for a defined concept instead, the list of types is _extended_ in a similar
|
|
|
+way to what is shown for the implementation of the above concept.<br/>
|
|
|
To do this, it's useful to declare a function that allows to convert a _concept_
|
|
|
into its underlying `type_list` object:
|
|
|
|
|
|
@@ -268,8 +267,8 @@ template<typename... Type>
|
|
|
entt::type_list<Type...> as_type_list(const entt::type_list<Type...> &);
|
|
|
```
|
|
|
|
|
|
-The definition isn't strictly required, since the function will only be used
|
|
|
-through a `decltype` as it follows:
|
|
|
+The definition isn't strictly required, since the function is only used through
|
|
|
+a `decltype` as it follows:
|
|
|
|
|
|
```cpp
|
|
|
struct DrawableAndErasable: entt::type_list_cat_t<
|
|
|
@@ -286,9 +285,8 @@ Everything else is the same as already shown instead.
|
|
|
|
|
|
# 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:
|
|
|
+Once the _concept_ and implementation are defined, it's possible to use the
|
|
|
+`poly` class template to _wrap_ instances that meet the requirements:
|
|
|
|
|
|
```cpp
|
|
|
using drawable = entt::poly<Drawable>;
|
|
|
@@ -310,9 +308,9 @@ instance = square{};
|
|
|
instance->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
|
|
|
-constructors, as well as the ability to create objects in-place.<br/>
|
|
|
+This class offers a wide range of constructors, from the default one (which
|
|
|
+returns an uninitialized `poly` object) to the copy and move constructors, as
|
|
|
+well as the ability to create objects in-place.<br/>
|
|
|
Among others, there is also a constructor that allows users to wrap unmanaged
|
|
|
objects in a `poly` instance (either const or non-const ones):
|
|
|
|
|
|
@@ -329,14 +327,14 @@ drawable other = instance.as_ref();
|
|
|
```
|
|
|
|
|
|
In both cases, although the interface of the `poly` object doesn't change, it
|
|
|
-won't construct any element or take care of destroying the referenced objects.
|
|
|
+doesn't construct any element or take care of destroying the referenced objects.
|
|
|
|
|
|
Note also how the underlying concept is accessed via a call to `operator->` and
|
|
|
not directly as `instance.draw()`.<br/>
|
|
|
This allows users to decouple the API of the wrapper from that of the concept.
|
|
|
-Therefore, where `instance.data()` will invoke the `data` member function of the
|
|
|
-poly object, `instance->data()` will map directly to the functionality exposed
|
|
|
-by the underlying concept.
|
|
|
+Therefore, where `instance.data()` invokes the `data` member function of the
|
|
|
+poly object, `instance->data()` maps directly to the functionality exposed by
|
|
|
+the underlying concept.
|
|
|
|
|
|
# Storage size and alignment requirement
|
|
|
|
|
|
@@ -351,9 +349,9 @@ entt::basic_poly<Drawable, sizeof(double[4]), alignof(double[4])>
|
|
|
|
|
|
The default size is `sizeof(double[2])`, which seems like a good compromise
|
|
|
between a buffer that is too large and one unable to hold anything larger than
|
|
|
-an integer. The alignment requirement is optional instead and by default such
|
|
|
-that it's the most stringent (the largest) for any object whose size is at most
|
|
|
-equal to the one provided.<br/>
|
|
|
+an integer. The alignment requirement is optional and by default such that it's
|
|
|
+the most stringent (the largest) for any object whose size is at most equal to
|
|
|
+the one provided.<br/>
|
|
|
It's worth noting that providing a size of 0 (which is an accepted value in all
|
|
|
respects) will force the system to dynamically allocate the contained objects in
|
|
|
all cases.
|