locator.hpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. #ifndef ENTT_LOCATOR_LOCATOR_HPP
  2. #define ENTT_LOCATOR_LOCATOR_HPP
  3. #include <memory>
  4. #include <utility>
  5. #include "../config/config.h"
  6. namespace entt {
  7. /**
  8. * @brief Service locator, nothing more.
  9. *
  10. * A service locator is used to do what it promises: locate services.<br/>
  11. * Usually service locators are tightly bound to the services they expose and
  12. * thus it's hard to define a general purpose class to do that. This tiny class
  13. * tries to fill the gap and to get rid of the burden of defining a different
  14. * specific locator for each application.
  15. *
  16. * @note
  17. * Users shouldn't retain references to a service. The recommended way is to
  18. * retrieve the service implementation currently set each and every time the
  19. * need for it arises. The risk is to incur in unexpected behaviors otherwise.
  20. *
  21. * @tparam Service Service type.
  22. */
  23. template<typename Service>
  24. class locator final {
  25. class service_handle {
  26. friend class locator<Service>;
  27. std::shared_ptr<Service> value{};
  28. };
  29. public:
  30. /*! @brief Service type. */
  31. using type = Service;
  32. /*! @brief Service node type. */
  33. using node_type = service_handle;
  34. /*! @brief Default constructor, deleted on purpose. */
  35. locator() = delete;
  36. /*! @brief Default destructor, deleted on purpose. */
  37. ~locator() = delete;
  38. /**
  39. * @brief Checks whether a service locator contains a value.
  40. * @return True if the service locator contains a value, false otherwise.
  41. */
  42. [[nodiscard]] static bool has_value() noexcept {
  43. return (service != nullptr);
  44. }
  45. /**
  46. * @brief Returns a reference to a valid service, if any.
  47. *
  48. * @warning
  49. * Invoking this function can result in undefined behavior if the service
  50. * hasn't been set yet.
  51. *
  52. * @return A reference to the service currently set, if any.
  53. */
  54. [[nodiscard]] static Service &value() noexcept {
  55. ENTT_ASSERT(has_value(), "Service not available");
  56. return *service;
  57. }
  58. /**
  59. * @brief Returns a service if available or sets it from a fallback type.
  60. *
  61. * Arguments are used only if a service doesn't already exist. In all other
  62. * cases, they are discarded.
  63. *
  64. * @tparam Args Types of arguments to use to construct the fallback service.
  65. * @tparam Type Fallback service type.
  66. * @param args Parameters to use to construct the fallback service.
  67. * @return A reference to a valid service.
  68. */
  69. template<typename Type = Service, typename... Args>
  70. [[nodiscard]] static Service &value_or(Args &&...args) {
  71. return service ? *service : emplace<Type>(std::forward<Args>(args)...);
  72. }
  73. /**
  74. * @brief Sets or replaces a service.
  75. * @tparam Type Service type.
  76. * @tparam Args Types of arguments to use to construct the service.
  77. * @param args Parameters to use to construct the service.
  78. * @return A reference to a valid service.
  79. */
  80. template<typename Type = Service, typename... Args>
  81. static Service &emplace(Args &&...args) {
  82. service = std::make_shared<Type>(std::forward<Args>(args)...);
  83. return *service;
  84. }
  85. /**
  86. * @brief Sets or replaces a service using a given allocator.
  87. * @tparam Type Service type.
  88. * @tparam Allocator Type of allocator used to manage memory and elements.
  89. * @tparam Args Types of arguments to use to construct the service.
  90. * @param alloc The allocator to use.
  91. * @param args Parameters to use to construct the service.
  92. * @return A reference to a valid service.
  93. */
  94. template<typename Type = Service, typename Allocator, typename... Args>
  95. static Service &emplace(std::allocator_arg_t, Allocator alloc, Args &&...args) {
  96. service = std::allocate_shared<Type>(alloc, std::forward<Args>(args)...);
  97. return *service;
  98. }
  99. /**
  100. * @brief Returns a handle to the underlying service.
  101. * @return A handle to the underlying service.
  102. */
  103. static node_type handle() noexcept {
  104. node_type node{};
  105. node.value = service;
  106. return node;
  107. }
  108. /**
  109. * @brief Resets or replaces a service.
  110. * @param other Optional handle with which to replace the service.
  111. */
  112. static void reset(const node_type &other = {}) noexcept {
  113. service = other.value;
  114. }
  115. /**
  116. * @brief Resets or replaces a service.
  117. * @tparam Type Service type.
  118. * @tparam Deleter Deleter type.
  119. * @param elem A pointer to a service to manage.
  120. * @param deleter A deleter to use to destroy the service.
  121. */
  122. template<typename Type, typename Deleter = std::default_delete<Type>>
  123. static void reset(Type *elem, Deleter deleter = {}) {
  124. service = std::shared_ptr<Service>{elem, std::move(deleter)};
  125. }
  126. private:
  127. // std::shared_ptr because of its type erased allocator which is useful here
  128. inline static std::shared_ptr<Service> service{};
  129. };
  130. } // namespace entt
  131. #endif