poly: added configurable sbo size (0 -> forced dyn)

This commit is contained in:
Michele Caini
2021-02-20 00:12:43 +01:00
parent 42afeef993
commit ccdaec86b3
4 changed files with 55 additions and 6 deletions

View File

@@ -13,6 +13,7 @@
* [Fullfill a concept](#fullfill-a-concept)
* [Inheritance](#inheritance)
* [Static polymorphism in the wild](#static-polymorphism-in-the-wild)
* [Configurable storage size](#configurable-storage-size)
<!--
@endcond TURN_OFF_DOXYGEN
-->
@@ -236,7 +237,7 @@ For a deduced concept, inheritance is achieved in a few steps:
```cpp
struct DrawableAndErasable: entt::type_list<> {
template<typename Base>
struct type: typename Drawable::type<Base> {
struct type: typename Drawable::template type<Base> {
static constexpr auto base = std::tuple_size_v<typename entt::poly_vtable<Drawable>::type>;
void erase() { entt::poly_call<base + 0>(*this); }
};
@@ -336,3 +337,22 @@ 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.
# Configurable storage size
Under the hood, the `poly` class template makes use of `entt::any`. Therefore,
it can take advantage of the possibility of defining at compile-time the size of
the storage suitable for the small buffer optimization.<br/>
To do this, it will be sufficient to provide the desired size as a second
template parameter:
```cpp
entt::poly<Drawable, sizeof(double[4])>
```
The default value 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.<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.

View File

@@ -171,17 +171,18 @@ decltype(auto) poly_call(Poly &&self, Args &&... args) {
* Moreover, the `poly` class template also works with unmanaged objects.
*
* @tparam Concept Concept descriptor.
* @tparam Len Size of the storage reserved for the small buffer optimization.
*/
template<typename Concept>
class poly: private Concept::template type<poly_base<poly<Concept>>> {
template<typename Concept, std::size_t Len = sizeof(double[2])>
class poly: private Concept::template type<poly_base<poly<Concept, Len>>> {
/*! @brief A poly base is allowed to snoop into a poly object. */
friend struct poly_base<poly<Concept>>;
friend struct poly_base<poly>;
using vtable_type = typename poly_vtable<Concept>::type;
public:
/*! @brief Concept type. */
using concept_type = typename Concept::template type<poly_base<poly<Concept>>>;
using concept_type = typename Concept::template type<poly_base<poly>>;
/*! @brief Default constructor. */
poly() ENTT_NOEXCEPT
@@ -336,7 +337,7 @@ public:
}
private:
any storage;
basic_any<Len> storage;
const vtable_type *vtable;
};

View File

@@ -208,3 +208,17 @@ TEST(PolyDeduced, AsRef) {
ASSERT_NE(ref.data(), nullptr);
ASSERT_NE(cref.data(), nullptr);
}
TEST(PolyDeduced, SBOVsZeroedSBOSize) {
entt::poly<Deduced> sbo{impl{}};
const auto broken = sbo.data();
entt::poly<Deduced> other = std::move(sbo);
ASSERT_NE(broken, other.data());
entt::poly<Deduced, 0u> dyn{impl{}};
const auto valid = dyn.data();
entt::poly<Deduced, 0u> same = std::move(dyn);
ASSERT_EQ(valid, same.data());
}

View File

@@ -214,3 +214,17 @@ TEST(PolyDefined, AsRef) {
ASSERT_NE(ref.data(), nullptr);
ASSERT_NE(cref.data(), nullptr);
}
TEST(PolyDefined, SBOVsZeroedSBOSize) {
entt::poly<Defined> sbo{impl{}};
const auto broken = sbo.data();
entt::poly<Defined> other = std::move(sbo);
ASSERT_NE(broken, other.data());
entt::poly<Defined, 0u> dyn{impl{}};
const auto valid = dyn.data();
entt::poly<Defined, 0u> same = std::move(dyn);
ASSERT_EQ(valid, same.data());
}