From fed6831cdc36e641b65baeca75ff999ec519efc6 Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Wed, 5 Apr 2023 14:20:34 +0200 Subject: [PATCH] locator: support to opaque structures (close #956) --- src/entt/locator/locator.hpp | 32 ++++++++---- test/entt/locator/locator.cpp | 94 +++++++++++++++++++++-------------- 2 files changed, 78 insertions(+), 48 deletions(-) diff --git a/src/entt/locator/locator.hpp b/src/entt/locator/locator.hpp index e33377ab7..6e020c3c7 100644 --- a/src/entt/locator/locator.hpp +++ b/src/entt/locator/locator.hpp @@ -70,40 +70,40 @@ public: * cases, they are discarded. * * @tparam Args Types of arguments to use to construct the fallback service. - * @tparam Impl Fallback service type. + * @tparam Type Fallback service type. * @param args Parameters to use to construct the fallback service. * @return A reference to a valid service. */ - template + template [[nodiscard]] static Service &value_or(Args &&...args) { - return service ? *service : emplace(std::forward(args)...); + return service ? *service : emplace(std::forward(args)...); } /** * @brief Sets or replaces a service. - * @tparam Impl Service type. + * @tparam Type Service type. * @tparam Args Types of arguments to use to construct the service. * @param args Parameters to use to construct the service. * @return A reference to a valid service. */ - template + template static Service &emplace(Args &&...args) { - service = std::make_shared(std::forward(args)...); + service = std::make_shared(std::forward(args)...); return *service; } /** * @brief Sets or replaces a service using a given allocator. - * @tparam Impl Service type. + * @tparam Type Service type. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the service. * @param alloc The allocator to use. * @param args Parameters to use to construct the service. * @return A reference to a valid service. */ - template - static Service &allocate_emplace(Allocator alloc, Args &&...args) { - service = std::allocate_shared(alloc, std::forward(args)...); + template + static Service &emplace(std::allocator_arg_t, Allocator alloc, Args &&...args) { + service = std::allocate_shared(alloc, std::forward(args)...); return *service; } @@ -125,6 +125,18 @@ public: service = other.value; } + /** + * @brief Resets or replaces a service. + * @tparam Type Service type. + * @tparam Deleter Deleter type. + * @param elem A pointer to a service to manage. + * @param deleter A deleter to use to destroy the service. + */ + template> + static void reset(Type *elem, Deleter deleter = {}) { + service = std::shared_ptr{elem, std::move(deleter)}; + } + private: // std::shared_ptr because of its type erased allocator which is useful here inline static std::shared_ptr service{}; diff --git a/test/entt/locator/locator.cpp b/test/entt/locator/locator.cpp index 31533de41..c62686054 100644 --- a/test/entt/locator/locator.cpp +++ b/test/entt/locator/locator.cpp @@ -5,36 +5,57 @@ struct base_service { virtual ~base_service() = default; - virtual void invoke() {} -}; - -struct null_service: base_service { - void invoke() override { - invoked = true; - } - - static inline bool invoked{}; + virtual int invoke(int) = 0; }; struct derived_service: base_service { - void invoke() override { - invoked = true; + derived_service(int val) + : value{val} {} + + int invoke(int other) override { + return value + other; } - static inline bool invoked{}; +private: + int value; }; -TEST(ServiceLocator, Functionalities) { - ASSERT_FALSE(entt::locator::has_value()); - ASSERT_FALSE(derived_service::invoked); - ASSERT_FALSE(null_service::invoked); +struct ServiceLocator: ::testing::Test { + void SetUp() override { + entt::locator::reset(); + } +}; - entt::locator::value_or().invoke(); +using ServiceLocatorDeathTest = ServiceLocator; + +TEST_F(ServiceLocator, ValueAndTheLike) { + ASSERT_FALSE(entt::locator::has_value()); + ASSERT_EQ(entt::locator::value_or(1).invoke(3), 4); + ASSERT_TRUE(entt::locator::has_value()); + ASSERT_EQ(entt::locator::value().invoke(9), 10); +} + +TEST_F(ServiceLocator, Emplace) { + ASSERT_FALSE(entt::locator::has_value()); + ASSERT_EQ(entt::locator::emplace(5).invoke(1), 6); + ASSERT_TRUE(entt::locator::has_value()); + ASSERT_EQ(entt::locator::value().invoke(3), 8); + + entt::locator::reset(); + + ASSERT_FALSE(entt::locator::has_value()); + ASSERT_EQ(entt::locator::emplace(std::allocator_arg, std::allocator{}, 5).invoke(1), 6); + ASSERT_TRUE(entt::locator::has_value()); + ASSERT_EQ(entt::locator::value().invoke(3), 8); +} + +TEST_F(ServiceLocator, ResetHandle) { + entt::locator::emplace(1); + auto handle = entt::locator::handle(); ASSERT_TRUE(entt::locator::has_value()); - ASSERT_TRUE(null_service::invoked); + ASSERT_EQ(entt::locator::value().invoke(3), 4); - auto handle = entt::locator::handle(); entt::locator::reset(); ASSERT_FALSE(entt::locator::has_value()); @@ -42,28 +63,25 @@ TEST(ServiceLocator, Functionalities) { entt::locator::reset(handle); ASSERT_TRUE(entt::locator::has_value()); - - entt::locator::reset(decltype(handle){}); - - ASSERT_FALSE(entt::locator::has_value()); - - entt::locator::emplace(); - entt::locator::value().invoke(); - - ASSERT_TRUE(entt::locator::has_value()); - ASSERT_TRUE(derived_service::invoked); - - derived_service::invoked = false; - entt::locator::allocate_emplace(std::allocator{}).invoke(); - - ASSERT_TRUE(entt::locator::has_value()); - ASSERT_TRUE(derived_service::invoked); + ASSERT_EQ(entt::locator::value().invoke(3), 4); } -ENTT_DEBUG_TEST(ServiceLocatorDeathTest, UninitializedValue) { - ASSERT_NO_FATAL_FAILURE(entt::locator::value_or().invoke()); +TEST_F(ServiceLocator, ElementWithDeleter) { + derived_service service{1}; + entt::locator::reset(&service, [](base_service *serv) { *static_cast(serv) = derived_service{2}; }); + + ASSERT_TRUE(entt::locator::has_value()); + ASSERT_EQ(entt::locator::value().invoke(1), 2); entt::locator::reset(); - ASSERT_DEATH(entt::locator::value().invoke(), ""); + ASSERT_EQ(service.invoke(1), 3); +} + +ENTT_DEBUG_TEST_F(ServiceLocatorDeathTest, UninitializedValue) { + ASSERT_EQ(entt::locator::value_or(1).invoke(1), 2); + + entt::locator::reset(); + + ASSERT_DEATH(entt::locator::value().invoke(42), ""); }