locator: support to opaque structures (close #956)

This commit is contained in:
Michele Caini
2023-04-05 14:20:34 +02:00
parent 1605c8d9d3
commit fed6831cdc
2 changed files with 78 additions and 48 deletions

View File

@@ -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<typename Impl = Service, typename... Args>
template<typename Type = Service, typename... Args>
[[nodiscard]] static Service &value_or(Args &&...args) {
return service ? *service : emplace<Impl>(std::forward<Args>(args)...);
return service ? *service : emplace<Type>(std::forward<Args>(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<typename Impl = Service, typename... Args>
template<typename Type = Service, typename... Args>
static Service &emplace(Args &&...args) {
service = std::make_shared<Impl>(std::forward<Args>(args)...);
service = std::make_shared<Type>(std::forward<Args>(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<typename Impl = Service, typename Allocator, typename... Args>
static Service &allocate_emplace(Allocator alloc, Args &&...args) {
service = std::allocate_shared<Impl>(alloc, std::forward<Args>(args)...);
template<typename Type = Service, typename Allocator, typename... Args>
static Service &emplace(std::allocator_arg_t, Allocator alloc, Args &&...args) {
service = std::allocate_shared<Type>(alloc, std::forward<Args>(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<typename Type, typename Deleter = std::default_delete<Type>>
static void reset(Type *elem, Deleter deleter = {}) {
service = std::shared_ptr<Service>{elem, std::move(deleter)};
}
private:
// std::shared_ptr because of its type erased allocator which is useful here
inline static std::shared_ptr<Service> service{};

View File

@@ -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<base_service>::has_value());
ASSERT_FALSE(derived_service::invoked);
ASSERT_FALSE(null_service::invoked);
struct ServiceLocator: ::testing::Test {
void SetUp() override {
entt::locator<base_service>::reset();
}
};
entt::locator<base_service>::value_or<null_service>().invoke();
using ServiceLocatorDeathTest = ServiceLocator;
TEST_F(ServiceLocator, ValueAndTheLike) {
ASSERT_FALSE(entt::locator<base_service>::has_value());
ASSERT_EQ(entt::locator<base_service>::value_or<derived_service>(1).invoke(3), 4);
ASSERT_TRUE(entt::locator<base_service>::has_value());
ASSERT_EQ(entt::locator<base_service>::value().invoke(9), 10);
}
TEST_F(ServiceLocator, Emplace) {
ASSERT_FALSE(entt::locator<base_service>::has_value());
ASSERT_EQ(entt::locator<base_service>::emplace<derived_service>(5).invoke(1), 6);
ASSERT_TRUE(entt::locator<base_service>::has_value());
ASSERT_EQ(entt::locator<base_service>::value().invoke(3), 8);
entt::locator<base_service>::reset();
ASSERT_FALSE(entt::locator<base_service>::has_value());
ASSERT_EQ(entt::locator<base_service>::emplace<derived_service>(std::allocator_arg, std::allocator<derived_service>{}, 5).invoke(1), 6);
ASSERT_TRUE(entt::locator<base_service>::has_value());
ASSERT_EQ(entt::locator<base_service>::value().invoke(3), 8);
}
TEST_F(ServiceLocator, ResetHandle) {
entt::locator<base_service>::emplace<derived_service>(1);
auto handle = entt::locator<base_service>::handle();
ASSERT_TRUE(entt::locator<base_service>::has_value());
ASSERT_TRUE(null_service::invoked);
ASSERT_EQ(entt::locator<base_service>::value().invoke(3), 4);
auto handle = entt::locator<base_service>::handle();
entt::locator<base_service>::reset();
ASSERT_FALSE(entt::locator<base_service>::has_value());
@@ -42,28 +63,25 @@ TEST(ServiceLocator, Functionalities) {
entt::locator<base_service>::reset(handle);
ASSERT_TRUE(entt::locator<base_service>::has_value());
entt::locator<base_service>::reset(decltype(handle){});
ASSERT_FALSE(entt::locator<base_service>::has_value());
entt::locator<base_service>::emplace<derived_service>();
entt::locator<base_service>::value().invoke();
ASSERT_TRUE(entt::locator<base_service>::has_value());
ASSERT_TRUE(derived_service::invoked);
derived_service::invoked = false;
entt::locator<base_service>::allocate_emplace<derived_service>(std::allocator<derived_service>{}).invoke();
ASSERT_TRUE(entt::locator<base_service>::has_value());
ASSERT_TRUE(derived_service::invoked);
ASSERT_EQ(entt::locator<base_service>::value().invoke(3), 4);
}
ENTT_DEBUG_TEST(ServiceLocatorDeathTest, UninitializedValue) {
ASSERT_NO_FATAL_FAILURE(entt::locator<base_service>::value_or().invoke());
TEST_F(ServiceLocator, ElementWithDeleter) {
derived_service service{1};
entt::locator<base_service>::reset(&service, [](base_service *serv) { *static_cast<derived_service *>(serv) = derived_service{2}; });
ASSERT_TRUE(entt::locator<base_service>::has_value());
ASSERT_EQ(entt::locator<base_service>::value().invoke(1), 2);
entt::locator<base_service>::reset();
ASSERT_DEATH(entt::locator<base_service>::value().invoke(), "");
ASSERT_EQ(service.invoke(1), 3);
}
ENTT_DEBUG_TEST_F(ServiceLocatorDeathTest, UninitializedValue) {
ASSERT_EQ(entt::locator<base_service>::value_or<derived_service>(1).invoke(1), 2);
entt::locator<base_service>::reset();
ASSERT_DEATH(entt::locator<base_service>::value().invoke(42), "");
}