ソースを参照

minimal locator implementation

Michele Caini 8 年 前
コミット
fb8745ccf0
2 ファイル変更164 行追加0 行削除
  1. 115 0
      src/entt/locator/locator.hpp
  2. 49 0
      test/entt/locator/locator.cpp

+ 115 - 0
src/entt/locator/locator.hpp

@@ -0,0 +1,115 @@
+#ifndef ENTT_LOCATOR_LOCATOR_HPP
+#define ENTT_LOCATOR_LOCATOR_HPP
+
+
+#include <memory>
+#include <utility>
+#include <cassert>
+
+
+namespace entt {
+
+
+/**
+ * @brief Service locator, nothing more.
+ *
+ * A service locator can be used to do what it promises: locate services.<br/>
+ * Usually service locators are tighly bound to the services they expose and
+ * thus it's hard to define a general purpose class to do that. This template
+ * based implementation tries to fill the gap and to get rid of the burden of
+ * defining a different specific locator for each application.
+ *
+ * @tparam Service Type of service managed by the locator.
+ */
+template<typename Service>
+struct ServiceLocator final {
+    /*! @brief Type of service offered. */
+    using service_type = Service;
+
+    /*! @brief Default constructor, deleted on purpose. */
+    ServiceLocator() = delete;
+    /*! @brief Default destructor, deleted on purpose. */
+    ~ServiceLocator() = delete;
+
+    /**
+     * @brief Tests if a valid service implementation is set.
+     * @return True if the service is set, false otherwise.
+     */
+    inline static bool empty() noexcept {
+        return !static_cast<bool>(service);
+    }
+
+    /**
+     * @brief Returns a weak pointer to a service implementation, if any.
+     *
+     * Clients of a service shouldn't retain references to it. The recommended
+     * way is to retrieve the service implementation currently set each and
+     * every time the need of using it arises. Otherwise users can incur in
+     * unexpected behaviors.
+     *
+     * @return A reference to the service implementation currently set, if any.
+     */
+    inline static std::weak_ptr<Service> get() noexcept {
+        return service;
+    }
+
+    /**
+     * @brief Returns a weak reference to a service implementation, if any.
+     *
+     * Clients of a service shouldn't retain references to it. The recommended
+     * way is to retrieve the service implementation currently set each and
+     * every time the need of using it arises. Otherwise users can incur in
+     * unexpected behaviors.
+     *
+     * @warning
+     * In case no service implementation has been set, a call to this function
+     * results in undefined behavior.
+     *
+     * @return A reference to the service implementation currently set, if any.
+     */
+    inline static Service & ref() noexcept {
+        return *service;
+    }
+
+    /**
+     * @brief Sets or replaces a service.
+     * @tparam Impl Type of the new service to use.
+     * @tparam Args Types of arguments to use to construct the service.
+     * @param args Parameters to use to construct the service.
+     */
+    template<typename Impl = Service, typename... Args>
+    inline static void set(Args&&... args) {
+        service = std::make_shared<Impl>(std::forward<Args>(args)...);
+    }
+
+    /**
+     * @brief Sets or replaces a service.
+     * @param ptr Service to use to replace the current one.
+     */
+    inline static void set(std::shared_ptr<Service> ptr) {
+        assert(static_cast<bool>(ptr));
+        service = std::move(ptr);
+    }
+
+    /**
+     * @brief Resets a service.
+     *
+     * The service is no longer valid after a reset.
+     */
+    inline static void reset() {
+        service.reset();
+    }
+
+private:
+    static std::shared_ptr<Service> service;
+};
+
+
+template<typename Service>
+std::shared_ptr<Service> ServiceLocator<Service>::service{};
+
+
+}
+
+
+#endif // ENTT_LOCATOR_LOCATOR_HPP

+ 49 - 0
test/entt/locator/locator.cpp

@@ -0,0 +1,49 @@
+#include <gtest/gtest.h>
+#include <entt/locator/locator.hpp>
+
+struct A {};
+
+struct B {
+    virtual void f(bool) = 0;
+    bool check{false};
+};
+
+struct D: B {
+    D(int): B{} {}
+    void f(bool b) override { check = b; }
+};
+
+TEST(ServiceLocator, Functionalities) {
+    using entt::ServiceLocator;
+
+    ASSERT_TRUE(ServiceLocator<A>::empty());
+    ASSERT_TRUE(ServiceLocator<B>::empty());
+
+    ServiceLocator<A>::set();
+
+    ASSERT_FALSE(ServiceLocator<A>::empty());
+    ASSERT_TRUE(ServiceLocator<B>::empty());
+
+    ServiceLocator<A>::reset();
+
+    ASSERT_TRUE(ServiceLocator<A>::empty());
+    ASSERT_TRUE(ServiceLocator<B>::empty());
+
+    ServiceLocator<A>::set(std::make_shared<A>());
+
+    ASSERT_FALSE(ServiceLocator<A>::empty());
+    ASSERT_TRUE(ServiceLocator<B>::empty());
+
+    ServiceLocator<B>::set<D>(42);
+
+    ASSERT_FALSE(ServiceLocator<A>::empty());
+    ASSERT_FALSE(ServiceLocator<B>::empty());
+
+    ServiceLocator<B>::get().lock()->f(!ServiceLocator<B>::get().lock()->check);
+
+    ASSERT_TRUE(ServiceLocator<B>::get().lock()->check);
+
+    ServiceLocator<B>::ref().f(!ServiceLocator<B>::get().lock()->check);
+
+    ASSERT_FALSE(ServiceLocator<B>::get().lock()->check);
+}