🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

490 lines
17 KiB

  1. #ifndef ENTT_CORE_ANY_HPP
  2. #define ENTT_CORE_ANY_HPP
  3. #include <cstddef>
  4. #include <memory>
  5. #include <type_traits>
  6. #include <utility>
  7. #include "../config/config.h"
  8. #include "../core/utility.hpp"
  9. #include "fwd.hpp"
  10. #include "type_info.hpp"
  11. #include "type_traits.hpp"
  12. namespace entt {
  13. /**
  14. * @brief A SBO friendly, type-safe container for single values of any type.
  15. * @tparam Len Size of the storage reserved for the small buffer optimization.
  16. * @tparam Align Optional alignment requirement.
  17. */
  18. template<std::size_t Len, std::size_t Align>
  19. class basic_any {
  20. enum class operation : std::uint8_t {
  21. copy,
  22. move,
  23. transfer,
  24. assign,
  25. destroy,
  26. compare,
  27. get
  28. };
  29. enum class policy : std::uint8_t {
  30. owner,
  31. ref,
  32. cref
  33. };
  34. using storage_type = std::aligned_storage_t<Len + !Len, Align>;
  35. using vtable_type = const void *(const operation, const basic_any &, const void *);
  36. template<typename Type>
  37. static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>;
  38. template<typename Type>
  39. static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] const void *other) {
  40. static_assert(!std::is_same_v<Type, void> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
  41. const Type *element = nullptr;
  42. if constexpr(in_situ<Type>) {
  43. element = value.owner() ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
  44. } else {
  45. element = static_cast<const Type *>(value.instance);
  46. }
  47. switch(op) {
  48. case operation::copy:
  49. if constexpr(std::is_copy_constructible_v<Type>) {
  50. static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*element);
  51. }
  52. break;
  53. case operation::move:
  54. if constexpr(in_situ<Type>) {
  55. if(value.owner()) {
  56. return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))};
  57. }
  58. }
  59. return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr));
  60. case operation::transfer:
  61. if constexpr(std::is_move_assignable_v<Type>) {
  62. *const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
  63. return other;
  64. }
  65. [[fallthrough]];
  66. case operation::assign:
  67. if constexpr(std::is_copy_assignable_v<Type>) {
  68. *const_cast<Type *>(element) = *static_cast<const Type *>(other);
  69. return other;
  70. }
  71. break;
  72. case operation::destroy:
  73. if constexpr(in_situ<Type>) {
  74. element->~Type();
  75. } else if constexpr(std::is_array_v<Type>) {
  76. delete[] element;
  77. } else {
  78. delete element;
  79. }
  80. break;
  81. case operation::compare:
  82. if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
  83. return *static_cast<const Type *>(element) == *static_cast<const Type *>(other) ? other : nullptr;
  84. } else {
  85. return (element == other) ? other : nullptr;
  86. }
  87. case operation::get:
  88. return element;
  89. }
  90. return nullptr;
  91. }
  92. template<typename Type, typename... Args>
  93. void initialize([[maybe_unused]] Args &&...args) {
  94. if constexpr(!std::is_void_v<Type>) {
  95. info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
  96. vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
  97. if constexpr(std::is_lvalue_reference_v<Type>) {
  98. static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments");
  99. mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref;
  100. instance = (std::addressof(args), ...);
  101. } else if constexpr(in_situ<Type>) {
  102. if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
  103. new(&storage) Type{std::forward<Args>(args)...};
  104. } else {
  105. new(&storage) Type(std::forward<Args>(args)...);
  106. }
  107. } else {
  108. if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
  109. instance = new Type{std::forward<Args>(args)...};
  110. } else {
  111. instance = new Type(std::forward<Args>(args)...);
  112. }
  113. }
  114. }
  115. }
  116. basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT
  117. : instance{other.data()},
  118. info{other.info},
  119. vtable{other.vtable},
  120. mode{pol} {}
  121. public:
  122. /*! @brief Size of the internal storage. */
  123. static constexpr auto length = Len;
  124. /*! @brief Alignment requirement. */
  125. static constexpr auto alignment = Align;
  126. /*! @brief Default constructor. */
  127. constexpr basic_any() ENTT_NOEXCEPT
  128. : instance{},
  129. info{&type_id<void>()},
  130. vtable{},
  131. mode{policy::owner} {}
  132. /**
  133. * @brief Constructs a wrapper by directly initializing the new object.
  134. * @tparam Type Type of object to use to initialize the wrapper.
  135. * @tparam Args Types of arguments to use to construct the new instance.
  136. * @param args Parameters to use to construct the instance.
  137. */
  138. template<typename Type, typename... Args>
  139. explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
  140. : basic_any{} {
  141. initialize<Type>(std::forward<Args>(args)...);
  142. }
  143. /**
  144. * @brief Constructs a wrapper from a given value.
  145. * @tparam Type Type of object to use to initialize the wrapper.
  146. * @param value An instance of an object to use to initialize the wrapper.
  147. */
  148. template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
  149. basic_any(Type &&value)
  150. : basic_any{} {
  151. initialize<std::decay_t<Type>>(std::forward<Type>(value));
  152. }
  153. /**
  154. * @brief Copy constructor.
  155. * @param other The instance to copy from.
  156. */
  157. basic_any(const basic_any &other)
  158. : basic_any{} {
  159. if(other.vtable) {
  160. other.vtable(operation::copy, other, this);
  161. }
  162. }
  163. /**
  164. * @brief Move constructor.
  165. * @param other The instance to move from.
  166. */
  167. basic_any(basic_any &&other) ENTT_NOEXCEPT
  168. : instance{},
  169. info{other.info},
  170. vtable{other.vtable},
  171. mode{other.mode} {
  172. if(other.vtable) {
  173. other.vtable(operation::move, other, this);
  174. }
  175. }
  176. /*! @brief Frees the internal storage, whatever it means. */
  177. ~basic_any() {
  178. if(vtable && owner()) {
  179. vtable(operation::destroy, *this, nullptr);
  180. }
  181. }
  182. /**
  183. * @brief Copy assignment operator.
  184. * @param other The instance to copy from.
  185. * @return This any object.
  186. */
  187. basic_any &operator=(const basic_any &other) {
  188. reset();
  189. if(other.vtable) {
  190. other.vtable(operation::copy, other, this);
  191. }
  192. return *this;
  193. }
  194. /**
  195. * @brief Move assignment operator.
  196. * @param other The instance to move from.
  197. * @return This any object.
  198. */
  199. basic_any &operator=(basic_any &&other) ENTT_NOEXCEPT {
  200. reset();
  201. if(other.vtable) {
  202. other.vtable(operation::move, other, this);
  203. info = other.info;
  204. vtable = other.vtable;
  205. mode = other.mode;
  206. }
  207. return *this;
  208. }
  209. /**
  210. * @brief Value assignment operator.
  211. * @tparam Type Type of object to use to initialize the wrapper.
  212. * @param value An instance of an object to use to initialize the wrapper.
  213. * @return This any object.
  214. */
  215. template<typename Type>
  216. std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>, basic_any &>
  217. operator=(Type &&value) {
  218. emplace<std::decay_t<Type>>(std::forward<Type>(value));
  219. return *this;
  220. }
  221. /**
  222. * @brief Returns the object type if any, `type_id<void>()` otherwise.
  223. * @return The object type if any, `type_id<void>()` otherwise.
  224. */
  225. [[nodiscard]] const type_info &type() const ENTT_NOEXCEPT {
  226. return *info;
  227. }
  228. /**
  229. * @brief Returns an opaque pointer to the contained instance.
  230. * @return An opaque pointer the contained instance, if any.
  231. */
  232. [[nodiscard]] const void *data() const ENTT_NOEXCEPT {
  233. return vtable ? vtable(operation::get, *this, nullptr) : nullptr;
  234. }
  235. /**
  236. * @brief Returns an opaque pointer to the contained instance.
  237. * @param req Expected type.
  238. * @return An opaque pointer the contained instance, if any.
  239. */
  240. [[nodiscard]] const void *data(const type_info &req) const ENTT_NOEXCEPT {
  241. return *info == req ? data() : nullptr;
  242. }
  243. /**
  244. * @brief Returns an opaque pointer to the contained instance.
  245. * @return An opaque pointer the contained instance, if any.
  246. */
  247. [[nodiscard]] void *data() ENTT_NOEXCEPT {
  248. return (!vtable || mode == policy::cref) ? nullptr : const_cast<void *>(vtable(operation::get, *this, nullptr));
  249. }
  250. /**
  251. * @brief Returns an opaque pointer to the contained instance.
  252. * @param req Expected type.
  253. * @return An opaque pointer the contained instance, if any.
  254. */
  255. [[nodiscard]] void *data(const type_info &req) ENTT_NOEXCEPT {
  256. return *info == req ? data() : nullptr;
  257. }
  258. /**
  259. * @brief Replaces the contained object by creating a new instance directly.
  260. * @tparam Type Type of object to use to initialize the wrapper.
  261. * @tparam Args Types of arguments to use to construct the new instance.
  262. * @param args Parameters to use to construct the instance.
  263. */
  264. template<typename Type, typename... Args>
  265. void emplace(Args &&...args) {
  266. reset();
  267. initialize<Type>(std::forward<Args>(args)...);
  268. }
  269. /**
  270. * @brief Assigns a value to the contained object without replacing it.
  271. * @param other The value to assign to the contained object.
  272. * @return True in case of success, false otherwise.
  273. */
  274. bool assign(const any &other) {
  275. if(vtable && mode != policy::cref && *info == *other.info) {
  276. return (vtable(operation::assign, *this, other.data()) != nullptr);
  277. }
  278. return false;
  279. }
  280. /*! @copydoc assign */
  281. bool assign(any &&other) {
  282. if(vtable && mode != policy::cref && *info == *other.info) {
  283. if(auto *val = other.data(); val) {
  284. return (vtable(operation::transfer, *this, val) != nullptr);
  285. } else {
  286. return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
  287. }
  288. }
  289. return false;
  290. }
  291. /*! @brief Destroys contained object */
  292. void reset() {
  293. if(vtable && owner()) {
  294. vtable(operation::destroy, *this, nullptr);
  295. }
  296. info = &type_id<void>();
  297. vtable = nullptr;
  298. mode = policy::owner;
  299. }
  300. /**
  301. * @brief Returns false if a wrapper is empty, true otherwise.
  302. * @return False if the wrapper is empty, true otherwise.
  303. */
  304. [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
  305. return vtable != nullptr;
  306. }
  307. /**
  308. * @brief Checks if two wrappers differ in their content.
  309. * @param other Wrapper with which to compare.
  310. * @return False if the two objects differ in their content, true otherwise.
  311. */
  312. bool operator==(const basic_any &other) const ENTT_NOEXCEPT {
  313. if(vtable && *info == *other.info) {
  314. return (vtable(operation::compare, *this, other.data()) != nullptr);
  315. }
  316. return (!vtable && !other.vtable);
  317. }
  318. /**
  319. * @brief Aliasing constructor.
  320. * @return A wrapper that shares a reference to an unmanaged object.
  321. */
  322. [[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT {
  323. return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)};
  324. }
  325. /*! @copydoc as_ref */
  326. [[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT {
  327. return basic_any{*this, policy::cref};
  328. }
  329. /**
  330. * @brief Returns true if a wrapper owns its object, false otherwise.
  331. * @return True if the wrapper owns its object, false otherwise.
  332. */
  333. [[nodiscard]] bool owner() const ENTT_NOEXCEPT {
  334. return (mode == policy::owner);
  335. }
  336. private:
  337. union {
  338. const void *instance;
  339. storage_type storage;
  340. };
  341. const type_info *info;
  342. vtable_type *vtable;
  343. policy mode;
  344. };
  345. /**
  346. * @brief Checks if two wrappers differ in their content.
  347. * @tparam Len Size of the storage reserved for the small buffer optimization.
  348. * @tparam Align Alignment requirement.
  349. * @param lhs A wrapper, either empty or not.
  350. * @param rhs A wrapper, either empty or not.
  351. * @return True if the two wrappers differ in their content, false otherwise.
  352. */
  353. template<std::size_t Len, std::size_t Align>
  354. [[nodiscard]] inline bool operator!=(const basic_any<Len, Align> &lhs, const basic_any<Len, Align> &rhs) ENTT_NOEXCEPT {
  355. return !(lhs == rhs);
  356. }
  357. /**
  358. * @brief Performs type-safe access to the contained object.
  359. * @tparam Type Type to which conversion is required.
  360. * @tparam Len Size of the storage reserved for the small buffer optimization.
  361. * @tparam Align Alignment requirement.
  362. * @param data Target any object.
  363. * @return The element converted to the requested type.
  364. */
  365. template<typename Type, std::size_t Len, std::size_t Align>
  366. Type any_cast(const basic_any<Len, Align> &data) ENTT_NOEXCEPT {
  367. const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
  368. ENTT_ASSERT(instance, "Invalid instance");
  369. return static_cast<Type>(*instance);
  370. }
  371. /*! @copydoc any_cast */
  372. template<typename Type, std::size_t Len, std::size_t Align>
  373. Type any_cast(basic_any<Len, Align> &data) ENTT_NOEXCEPT {
  374. // forces const on non-reference types to make them work also with wrappers for const references
  375. auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data);
  376. ENTT_ASSERT(instance, "Invalid instance");
  377. return static_cast<Type>(*instance);
  378. }
  379. /*! @copydoc any_cast */
  380. template<typename Type, std::size_t Len, std::size_t Align>
  381. Type any_cast(basic_any<Len, Align> &&data) ENTT_NOEXCEPT {
  382. if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
  383. if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
  384. return static_cast<Type>(std::move(*instance));
  385. } else {
  386. return any_cast<Type>(data);
  387. }
  388. } else {
  389. auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
  390. ENTT_ASSERT(instance, "Invalid instance");
  391. return static_cast<Type>(std::move(*instance));
  392. }
  393. }
  394. /*! @copydoc any_cast */
  395. template<typename Type, std::size_t Len, std::size_t Align>
  396. const Type *any_cast(const basic_any<Len, Align> *data) ENTT_NOEXCEPT {
  397. const auto &info = type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
  398. return static_cast<const Type *>(data->data(info));
  399. }
  400. /*! @copydoc any_cast */
  401. template<typename Type, std::size_t Len, std::size_t Align>
  402. Type *any_cast(basic_any<Len, Align> *data) ENTT_NOEXCEPT {
  403. const auto &info = type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
  404. // last attempt to make wrappers for const references return their values
  405. return static_cast<Type *>(static_cast<constness_as_t<basic_any<Len, Align>, Type> *>(data)->data(info));
  406. }
  407. /**
  408. * @brief Constructs a wrapper from a given type, passing it all arguments.
  409. * @tparam Type Type of object to use to initialize the wrapper.
  410. * @tparam Len Size of the storage reserved for the small buffer optimization.
  411. * @tparam Align Optional alignment requirement.
  412. * @tparam Args Types of arguments to use to construct the new instance.
  413. * @param args Parameters to use to construct the instance.
  414. * @return A properly initialized wrapper for an object of the given type.
  415. */
  416. template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args>
  417. basic_any<Len, Align> make_any(Args &&...args) {
  418. return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
  419. }
  420. /**
  421. * @brief Forwards its argument and avoids copies for lvalue references.
  422. * @tparam Len Size of the storage reserved for the small buffer optimization.
  423. * @tparam Align Optional alignment requirement.
  424. * @tparam Type Type of argument to use to construct the new instance.
  425. * @param value Parameter to use to construct the instance.
  426. * @return A properly initialized and not necessarily owning wrapper.
  427. */
  428. template<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename Type>
  429. basic_any<Len, Align> forward_as_any(Type &&value) {
  430. return basic_any<Len, Align>{std::in_place_type<std::conditional_t<std::is_rvalue_reference_v<Type>, std::decay_t<Type>, Type>>, std::forward<Type>(value)};
  431. }
  432. } // namespace entt
  433. #endif