diff --git a/docs/md/poly.md b/docs/md/poly.md index c87804e14..1c37be2cc 100644 --- a/docs/md/poly.md +++ b/docs/md/poly.md @@ -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.
-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.
-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.
+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.
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.
-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.
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.
+Once the interface is defined, a generic implementation is needed to fulfill the +concept itself.
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.
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 { Why should a user fully define a concept if the function types are the same as the deduced ones?
-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.
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.
+interface, even if they're part of the types that fulfill the concept.
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.
+In this case, it's stated that the `draw` method of a generic type is enough to +satisfy the requirements of the `Drawable` concept.
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.
-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.
+_size_ of the static virtual table of the base class is used as an offset for +the local indexes.
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.
+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.
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 entt::type_list as_type_list(const entt::type_list &); ``` -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; @@ -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.
+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.
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()`.
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 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.
+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.
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.