poly.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. #ifndef ENTT_POLY_POLY_HPP
  2. #define ENTT_POLY_POLY_HPP
  3. #include <cstddef>
  4. #include <functional>
  5. #include <tuple>
  6. #include <type_traits>
  7. #include <utility>
  8. #include "../core/any.hpp"
  9. #include "../core/type_info.hpp"
  10. #include "../core/type_traits.hpp"
  11. #include "fwd.hpp"
  12. namespace entt {
  13. /*! @brief Inspector class used to infer the type of the virtual table. */
  14. struct poly_inspector {
  15. /**
  16. * @brief Generic conversion operator (definition only).
  17. * @tparam Type Type to which conversion is requested.
  18. */
  19. template<typename Type>
  20. operator Type &&() const;
  21. /**
  22. * @brief Dummy invocation function (definition only).
  23. * @tparam Member Index of the function to invoke.
  24. * @tparam Args Types of arguments to pass to the function.
  25. * @param args The arguments to pass to the function.
  26. * @return A poly inspector convertible to any type.
  27. */
  28. template<std::size_t Member, typename... Args>
  29. poly_inspector invoke(Args &&...args) const;
  30. /*! @copydoc invoke */
  31. template<std::size_t Member, typename... Args>
  32. poly_inspector invoke(Args &&...args);
  33. };
  34. /**
  35. * @brief Static virtual table factory.
  36. * @tparam Concept Concept descriptor.
  37. * @tparam Len Size of the storage reserved for the small buffer optimization.
  38. * @tparam Align Alignment requirement.
  39. */
  40. template<typename Concept, std::size_t Len, std::size_t Align>
  41. class poly_vtable {
  42. using inspector = typename Concept::template type<poly_inspector>;
  43. template<typename Ret, typename... Args>
  44. static auto vtable_entry(Ret (*)(inspector &, Args...)) -> Ret (*)(basic_any<Len, Align> &, Args...);
  45. template<typename Ret, typename... Args>
  46. static auto vtable_entry(Ret (*)(const inspector &, Args...)) -> Ret (*)(const basic_any<Len, Align> &, Args...);
  47. template<typename Ret, typename... Args>
  48. static auto vtable_entry(Ret (*)(Args...)) -> Ret (*)(const basic_any<Len, Align> &, Args...);
  49. template<typename Ret, typename... Args>
  50. static auto vtable_entry(Ret (inspector::*)(Args...)) -> Ret (*)(basic_any<Len, Align> &, Args...);
  51. template<typename Ret, typename... Args>
  52. static auto vtable_entry(Ret (inspector::*)(Args...) const) -> Ret (*)(const basic_any<Len, Align> &, Args...);
  53. template<auto... Candidate>
  54. static auto make_vtable(value_list<Candidate...>) noexcept
  55. -> decltype(std::make_tuple(vtable_entry(Candidate)...));
  56. template<typename... Func>
  57. [[nodiscard]] static constexpr auto make_vtable(type_list<Func...>) noexcept {
  58. if constexpr(sizeof...(Func) == 0u) {
  59. return decltype(make_vtable(typename Concept::template impl<inspector>{})){};
  60. } else if constexpr((std::is_function_v<Func> && ...)) {
  61. return decltype(std::make_tuple(vtable_entry(std::declval<Func inspector::*>())...)){};
  62. }
  63. }
  64. template<typename Type, auto Candidate, typename Ret, typename Any, typename... Args>
  65. static void fill_vtable_entry(Ret (*&entry)(Any &, Args...)) noexcept {
  66. if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
  67. entry = +[](Any &, Args... args) -> Ret {
  68. return std::invoke(Candidate, std::forward<Args>(args)...);
  69. };
  70. } else {
  71. entry = +[](Any &instance, Args... args) -> Ret {
  72. return static_cast<Ret>(std::invoke(Candidate, any_cast<constness_as_t<Type, Any> &>(instance), std::forward<Args>(args)...));
  73. };
  74. }
  75. }
  76. template<typename Type, auto... Index>
  77. [[nodiscard]] static auto fill_vtable(std::index_sequence<Index...>) noexcept {
  78. vtable_type impl{};
  79. (fill_vtable_entry<Type, value_list_element_v<Index, typename Concept::template impl<Type>>>(std::get<Index>(impl)), ...);
  80. return impl;
  81. }
  82. using vtable_type = decltype(make_vtable(Concept{}));
  83. static constexpr bool is_mono_v = std::tuple_size_v<vtable_type> == 1u;
  84. public:
  85. /*! @brief Virtual table type. */
  86. using type = std::conditional_t<is_mono_v, std::tuple_element_t<0u, vtable_type>, const vtable_type *>;
  87. /**
  88. * @brief Returns a static virtual table for a specific concept and type.
  89. * @tparam Type The type for which to generate the virtual table.
  90. * @return A static virtual table for the given concept and type.
  91. */
  92. template<typename Type>
  93. [[nodiscard]] static type instance() noexcept {
  94. static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Type differs from its decayed form");
  95. static const vtable_type vtable = fill_vtable<Type>(std::make_index_sequence<Concept::template impl<Type>::size>{});
  96. if constexpr(is_mono_v) {
  97. return std::get<0>(vtable);
  98. } else {
  99. return &vtable;
  100. }
  101. }
  102. };
  103. /**
  104. * @brief Poly base class used to inject functionalities into concepts.
  105. * @tparam Poly The outermost poly class.
  106. */
  107. template<typename Poly>
  108. struct poly_base {
  109. /**
  110. * @brief Invokes a function from the static virtual table.
  111. * @tparam Member Index of the function to invoke.
  112. * @tparam Args Types of arguments to pass to the function.
  113. * @param self A reference to the poly object that made the call.
  114. * @param args The arguments to pass to the function.
  115. * @return The return value of the invoked function, if any.
  116. */
  117. template<std::size_t Member, typename... Args>
  118. [[nodiscard]] decltype(auto) invoke(const poly_base &self, Args &&...args) const {
  119. const auto &poly = static_cast<const Poly &>(self);
  120. if constexpr(std::is_function_v<std::remove_pointer_t<decltype(poly.vtable)>>) {
  121. return poly.vtable(poly.storage, std::forward<Args>(args)...);
  122. } else {
  123. return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...);
  124. }
  125. }
  126. /*! @copydoc invoke */
  127. template<std::size_t Member, typename... Args>
  128. [[nodiscard]] decltype(auto) invoke(poly_base &self, Args &&...args) {
  129. auto &poly = static_cast<Poly &>(self);
  130. if constexpr(std::is_function_v<std::remove_pointer_t<decltype(poly.vtable)>>) {
  131. static_assert(Member == 0u, "Unknown member");
  132. return poly.vtable(poly.storage, std::forward<Args>(args)...);
  133. } else {
  134. return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...);
  135. }
  136. }
  137. };
  138. /**
  139. * @brief Shortcut for calling `poly_base<Type>::invoke`.
  140. * @tparam Member Index of the function to invoke.
  141. * @tparam Poly A fully defined poly object.
  142. * @tparam Args Types of arguments to pass to the function.
  143. * @param self A reference to the poly object that made the call.
  144. * @param args The arguments to pass to the function.
  145. * @return The return value of the invoked function, if any.
  146. */
  147. template<std::size_t Member, typename Poly, typename... Args>
  148. decltype(auto) poly_call(Poly &&self, Args &&...args) {
  149. return std::forward<Poly>(self).template invoke<Member>(self, std::forward<Args>(args)...);
  150. }
  151. /**
  152. * @brief Static polymorphism made simple and within everyone's reach.
  153. *
  154. * Static polymorphism is a very powerful tool in C++, albeit sometimes
  155. * cumbersome to obtain.<br/>
  156. * This class aims to make it simple and easy to use.
  157. *
  158. * @note
  159. * Both deduced and defined static virtual tables are supported.<br/>
  160. * Moreover, the `poly` class template also works with unmanaged objects.
  161. *
  162. * @tparam Concept Concept descriptor.
  163. * @tparam Len Size of the storage reserved for the small buffer optimization.
  164. * @tparam Align Optional alignment requirement.
  165. */
  166. template<typename Concept, std::size_t Len, std::size_t Align>
  167. class basic_poly: private Concept::template type<poly_base<basic_poly<Concept, Len, Align>>> {
  168. friend struct poly_base<basic_poly>;
  169. public:
  170. /*! @brief Concept type. */
  171. using concept_type = typename Concept::template type<poly_base<basic_poly>>;
  172. /*! @brief Virtual table type. */
  173. using vtable_type = typename poly_vtable<Concept, Len, Align>::type;
  174. /*! @brief Default constructor. */
  175. basic_poly() noexcept
  176. : storage{},
  177. vtable{} {}
  178. /**
  179. * @brief Constructs a poly by directly initializing the new object.
  180. * @tparam Type Type of object to use to initialize the poly.
  181. * @tparam Args Types of arguments to use to construct the new instance.
  182. * @param args Parameters to use to construct the instance.
  183. */
  184. template<typename Type, typename... Args>
  185. explicit basic_poly(std::in_place_type_t<Type>, Args &&...args)
  186. : storage{std::in_place_type<Type>, std::forward<Args>(args)...},
  187. vtable{poly_vtable<Concept, Len, Align>::template instance<std::remove_cv_t<std::remove_reference_t<Type>>>()} {}
  188. /**
  189. * @brief Constructs a poly from a given value.
  190. * @tparam Type Type of object to use to initialize the poly.
  191. * @param value An instance of an object to use to initialize the poly.
  192. */
  193. template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, basic_poly>>>
  194. basic_poly(Type &&value) noexcept
  195. : basic_poly{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)} {}
  196. /**
  197. * @brief Returns the object type if any, `type_id<void>()` otherwise.
  198. * @return The object type if any, `type_id<void>()` otherwise.
  199. */
  200. [[nodiscard]] const type_info &type() const noexcept {
  201. return storage.type();
  202. }
  203. /**
  204. * @brief Returns an opaque pointer to the contained instance.
  205. * @return An opaque pointer the contained instance, if any.
  206. */
  207. [[nodiscard]] const void *data() const noexcept {
  208. return storage.data();
  209. }
  210. /*! @copydoc data */
  211. [[nodiscard]] void *data() noexcept {
  212. return storage.data();
  213. }
  214. /**
  215. * @brief Replaces the contained object by creating a new instance directly.
  216. * @tparam Type Type of object to use to initialize the poly.
  217. * @tparam Args Types of arguments to use to construct the new instance.
  218. * @param args Parameters to use to construct the instance.
  219. */
  220. template<typename Type, typename... Args>
  221. void emplace(Args &&...args) {
  222. storage.template emplace<Type>(std::forward<Args>(args)...);
  223. vtable = poly_vtable<Concept, Len, Align>::template instance<std::remove_cv_t<std::remove_reference_t<Type>>>();
  224. }
  225. /*! @brief Destroys contained object */
  226. void reset() {
  227. storage.reset();
  228. vtable = {};
  229. }
  230. /**
  231. * @brief Returns false if a poly is empty, true otherwise.
  232. * @return False if the poly is empty, true otherwise.
  233. */
  234. [[nodiscard]] explicit operator bool() const noexcept {
  235. return static_cast<bool>(storage);
  236. }
  237. /**
  238. * @brief Returns a pointer to the underlying concept.
  239. * @return A pointer to the underlying concept.
  240. */
  241. [[nodiscard]] concept_type *operator->() noexcept {
  242. return this;
  243. }
  244. /*! @copydoc operator-> */
  245. [[nodiscard]] const concept_type *operator->() const noexcept {
  246. return this;
  247. }
  248. /**
  249. * @brief Aliasing constructor.
  250. * @return A poly that shares a reference to an unmanaged object.
  251. */
  252. [[nodiscard]] basic_poly as_ref() noexcept {
  253. basic_poly ref{};
  254. ref.storage = storage.as_ref();
  255. ref.vtable = vtable;
  256. return ref;
  257. }
  258. /*! @copydoc as_ref */
  259. [[nodiscard]] basic_poly as_ref() const noexcept {
  260. basic_poly ref{};
  261. ref.storage = storage.as_ref();
  262. ref.vtable = vtable;
  263. return ref;
  264. }
  265. private:
  266. basic_any<Len, Align> storage;
  267. vtable_type vtable;
  268. };
  269. } // namespace entt
  270. #endif