Ver código fonte

memory: uses_allocator_construction_args (waiting for C++20)

Michele Caini 4 anos atrás
pai
commit
e08b1f82ce
2 arquivos alterados com 60 adições e 0 exclusões
  1. 30 0
      src/entt/core/memory.hpp
  2. 30 0
      test/entt/core/memory.cpp

+ 30 - 0
src/entt/core/memory.hpp

@@ -4,6 +4,7 @@
 #include <cstddef>
 #include <limits>
 #include <memory>
+#include <tuple>
 #include <type_traits>
 #include <utility>
 #include "../config/config.h"
@@ -162,6 +163,35 @@ auto allocate_unique(Allocator &allocator, Args &&...args) {
     return std::unique_ptr<Type, allocation_deleter<alloc>>{ptr, type_allocator};
 }
 
+/**
+ * @brief Uses-allocator construction utility.
+ *
+ * Primarily intended for internal use. Unlike the standard version (waiting for
+ * C++20), this utility does not differentiate between pair and non-pair types.
+ *
+ * @tparam Type Type to return arguments for.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ * @tparam Args Types of arguments to use to construct the object.
+ * @param allocator The allocator to use.
+ * @param args Parameters to use to construct the object.
+ * @return The arguments needed to create an object of the given type.
+ */
+template<typename Type, typename Allocator, typename... Args>
+constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT {
+    if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Args...>) {
+        return std::forward_as_tuple(std::forward<Args>(args)...);
+    } else {
+        static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request");
+
+        if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Args...>) {
+            return std::tuple<std::allocator_arg_t, const Allocator &, Args &&...>(std::allocator_arg, allocator, std::forward<Args>(args)...);
+        } else {
+            static_assert(std::is_constructible_v<Type, Args..., const Allocator &>, "Ill-formed request");
+            return std::forward_as_tuple(std::forward<Args>(args)..., allocator);
+        }
+    }
+}
+
 } // namespace entt
 
 #endif

+ 30 - 0
test/entt/core/memory.cpp

@@ -2,8 +2,10 @@
 #include <cstddef>
 #include <limits>
 #include <memory>
+#include <tuple>
 #include <type_traits>
 #include <utility>
+#include <vector>
 #include <gtest/gtest.h>
 #include <entt/core/memory.hpp>
 #include "../common/basic_test_allocator.hpp"
@@ -115,3 +117,31 @@ TEST(AllocateUnique, UsesAllocatorConstruction) {
 }
 
 #endif
+
+TEST(UsesAllocatorConstructionArgs, NoUsesAllocatorConstruction) {
+    const auto args = entt::uses_allocator_construction_args<int>(std::allocator<int>{}, 42);
+
+    static_assert(std::tuple_size_v<decltype(args)> == 1u);
+    static_assert(std::is_same_v<decltype(args), const std::tuple<int &&>>);
+
+    ASSERT_EQ(std::get<0>(args), 42);
+}
+
+TEST(UsesAllocatorConstructionArgs, LeadingAllocatorConvetion) {
+    const auto args = entt::uses_allocator_construction_args<std::tuple<int, char>>(std::allocator<int>{}, 42, 'c');
+
+    static_assert(std::tuple_size_v<decltype(args)> == 4u);
+    static_assert(std::is_same_v<decltype(args), const std::tuple<std::allocator_arg_t, const std::allocator<int> &, int &&, char &&>>);
+
+    ASSERT_EQ(std::get<2>(args), 42);
+    ASSERT_EQ(std::get<3>(args), 'c');
+}
+
+TEST(UsesAllocatorConstructionArgs, TrailingAllocatorConvetion) {
+    const auto args = entt::uses_allocator_construction_args<std::vector<int>>(std::allocator<int>{}, 42);
+
+    static_assert(std::tuple_size_v<decltype(args)> == 2u);
+    static_assert(std::is_same_v<decltype(args), const std::tuple<int &&, const std::allocator<int> &>>);
+
+    ASSERT_EQ(std::get<0>(args), 42);
+}