any.hpp 18 KB

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