Quellcode durchsuchen

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

Michele Caini vor 5 Jahren
Ursprung
Commit
ccdaec86b3
4 geänderte Dateien mit 55 neuen und 6 gelöschten Zeilen
  1. 21 1
      docs/md/poly.md
  2. 6 5
      src/entt/poly/poly.hpp
  3. 14 0
      test/entt/poly/poly_deduced.cpp
  4. 14 0
      test/entt/poly/poly_defined.cpp

+ 21 - 1
docs/md/poly.md

@@ -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.

+ 6 - 5
src/entt/poly/poly.hpp

@@ -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;
 };
 

+ 14 - 0
test/entt/poly/poly_deduced.cpp

@@ -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());
+}

+ 14 - 0
test/entt/poly/poly_defined.cpp

@@ -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());
+}