xany.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. /***************************************************************************
  2. * Copyright (c) Sylvain Corlay and Johan Mabille and Wolf Vollprecht *
  3. * Copyright (c) QuantStack *
  4. * *
  5. * Distributed under the terms of the BSD 3-Clause License. *
  6. * *
  7. * The full license is in the file LICENSE, distributed with this software. *
  8. ****************************************************************************/
  9. #ifndef XTL_ANY_HPP
  10. #define XTL_ANY_HPP
  11. #include <exception>
  12. #include <stdexcept>
  13. #include <type_traits>
  14. #include <typeinfo>
  15. #include "xtl/xmeta_utils.hpp"
  16. namespace xtl
  17. {
  18. /**************************************
  19. * Implementation of C++17's std::any *
  20. **************************************/
  21. // Copyright (c) 2016 Denilson das Mercês Amorim
  22. //
  23. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  24. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  25. class bad_any_cast : public std::bad_cast
  26. {
  27. public:
  28. const char* what() const noexcept override
  29. {
  30. return "bad any cast";
  31. }
  32. };
  33. namespace detail {
  34. inline static void check_any_cast(const void* p) {
  35. if (p == nullptr) {
  36. #if defined(XTL_NO_EXCEPTIONS)
  37. std::fprintf(stderr, "bad_any_cast\n");
  38. std::terminate();
  39. #else
  40. throw bad_any_cast();
  41. #endif
  42. }
  43. }
  44. } // namespace detail
  45. class any final
  46. {
  47. public:
  48. /// Constructs an object of type any with an empty state.
  49. any()
  50. : vtable(nullptr)
  51. {
  52. }
  53. /// Constructs an object of type any with an equivalent state as other.
  54. any(const any& rhs)
  55. : vtable(rhs.vtable)
  56. {
  57. if (!rhs.empty())
  58. {
  59. rhs.vtable->copy(rhs.storage, this->storage);
  60. }
  61. }
  62. /// Constructs an object of type any with a state equivalent to the original state of other.
  63. /// rhs is left in a valid but otherwise unspecified state.
  64. any(any&& rhs) noexcept
  65. : vtable(rhs.vtable)
  66. {
  67. if (!rhs.empty())
  68. {
  69. rhs.vtable->move(rhs.storage, this->storage);
  70. rhs.vtable = nullptr;
  71. }
  72. }
  73. /// Same effect as this->clear().
  74. ~any()
  75. {
  76. this->clear();
  77. }
  78. /// Constructs an object of type any that contains an object of type T direct-initialized with std::forward<ValueType>(value).
  79. ///
  80. /// T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed.
  81. /// This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed.
  82. template <typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, any>::value>::type>
  83. any(ValueType&& value)
  84. {
  85. static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
  86. "T shall satisfy the CopyConstructible requirements.");
  87. this->construct(std::forward<ValueType>(value));
  88. }
  89. /// Has the same effect as any(rhs).swap(*this). No effects if an exception is thrown.
  90. any& operator=(const any& rhs)
  91. {
  92. any(rhs).swap(*this);
  93. return *this;
  94. }
  95. /// Has the same effect as any(std::move(rhs)).swap(*this).
  96. ///
  97. /// The state of *this is equivalent to the original state of rhs and rhs is left in a valid
  98. /// but otherwise unspecified state.
  99. any& operator=(any&& rhs) noexcept
  100. {
  101. any(std::move(rhs)).swap(*this);
  102. return *this;
  103. }
  104. /// Has the same effect as any(std::forward<ValueType>(value)).swap(*this). No effect if a exception is thrown.
  105. ///
  106. /// T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed.
  107. /// This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed.
  108. template <typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, any>::value>::type>
  109. any& operator=(ValueType&& value)
  110. {
  111. static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
  112. "T shall satisfy the CopyConstructible requirements.");
  113. any(std::forward<ValueType>(value)).swap(*this);
  114. return *this;
  115. }
  116. /// If not empty, destroys the contained object.
  117. void clear() noexcept
  118. {
  119. if (!empty())
  120. {
  121. this->vtable->destroy(storage);
  122. this->vtable = nullptr;
  123. }
  124. }
  125. /// C++17 equivalent of clear
  126. void reset() noexcept
  127. {
  128. clear();
  129. }
  130. /// Returns true if *this has no contained object, otherwise false.
  131. bool empty() const noexcept
  132. {
  133. return this->vtable == nullptr;
  134. }
  135. /// C++17 equivalent of !empty()
  136. bool has_value() const noexcept
  137. {
  138. return !empty();
  139. }
  140. /// If *this has a contained object of type T, typeid(T); otherwise typeid(void).
  141. const std::type_info& type() const noexcept
  142. {
  143. return empty() ? typeid(void) : this->vtable->type();
  144. }
  145. /// Exchange the states of *this and rhs.
  146. void swap(any& rhs) noexcept
  147. {
  148. if (this->vtable != rhs.vtable)
  149. {
  150. any tmp(std::move(rhs));
  151. // move from *this to rhs.
  152. rhs.vtable = this->vtable;
  153. if (this->vtable != nullptr)
  154. {
  155. this->vtable->move(this->storage, rhs.storage);
  156. //this->vtable = nullptr; -- uneeded, see below
  157. }
  158. // move from tmp (previously rhs) to *this.
  159. this->vtable = tmp.vtable;
  160. if (tmp.vtable != nullptr)
  161. {
  162. tmp.vtable->move(tmp.storage, this->storage);
  163. tmp.vtable = nullptr;
  164. }
  165. }
  166. else // same types
  167. {
  168. if (this->vtable != nullptr)
  169. this->vtable->swap(this->storage, rhs.storage);
  170. }
  171. }
  172. private: // Storage and Virtual Method Table
  173. union storage_union {
  174. using stack_storage_t = typename std::aligned_storage<2 * sizeof(void*), std::alignment_of<void*>::value>::type;
  175. void* dynamic;
  176. stack_storage_t stack; // 2 words for e.g. shared_ptr
  177. };
  178. /// Base VTable specification.
  179. struct vtable_type
  180. {
  181. // Note: The caller is responssible for doing .vtable = nullptr after destructful operations
  182. // such as destroy() and/or move().
  183. /// The type of the object this vtable is for.
  184. const std::type_info& (*type)() noexcept;
  185. /// Destroys the object in the union.
  186. /// The state of the union after this call is unspecified, caller must ensure not to use src anymore.
  187. void (*destroy)(storage_union&) noexcept;
  188. /// Copies the **inner** content of the src union into the yet unitialized dest union.
  189. /// As such, both inner objects will have the same state, but on separate memory locations.
  190. void (*copy)(const storage_union& src, storage_union& dest);
  191. /// Moves the storage from src to the yet unitialized dest union.
  192. /// The state of src after this call is unspecified, caller must ensure not to use src anymore.
  193. void (*move)(storage_union& src, storage_union& dest) noexcept;
  194. /// Exchanges the storage between lhs and rhs.
  195. void (*swap)(storage_union& lhs, storage_union& rhs) noexcept;
  196. };
  197. /// VTable for dynamically allocated storage.
  198. template <typename T>
  199. struct vtable_dynamic
  200. {
  201. static const std::type_info& type() noexcept
  202. {
  203. return typeid(T);
  204. }
  205. static void destroy(storage_union& storage) noexcept
  206. {
  207. //assert(reinterpret_cast<T*>(storage.dynamic));
  208. delete reinterpret_cast<T*>(storage.dynamic);
  209. }
  210. static void copy(const storage_union& src, storage_union& dest)
  211. {
  212. dest.dynamic = new T(*reinterpret_cast<const T*>(src.dynamic));
  213. }
  214. static void move(storage_union& src, storage_union& dest) noexcept
  215. {
  216. dest.dynamic = src.dynamic;
  217. src.dynamic = nullptr;
  218. }
  219. static void swap(storage_union& lhs, storage_union& rhs) noexcept
  220. {
  221. // just exchage the storage pointers.
  222. std::swap(lhs.dynamic, rhs.dynamic);
  223. }
  224. };
  225. /// VTable for stack allocated storage.
  226. template <typename T>
  227. struct vtable_stack
  228. {
  229. static const std::type_info& type() noexcept
  230. {
  231. return typeid(T);
  232. }
  233. static void destroy(storage_union& storage) noexcept
  234. {
  235. reinterpret_cast<T*>(&storage.stack)->~T();
  236. }
  237. static void copy(const storage_union& src, storage_union& dest)
  238. {
  239. new (&dest.stack) T(reinterpret_cast<const T&>(src.stack));
  240. }
  241. static void move(storage_union& src, storage_union& dest) noexcept
  242. {
  243. // one of the conditions for using vtable_stack is a nothrow move constructor,
  244. // so this move constructor will never throw a exception.
  245. new (&dest.stack) T(std::move(reinterpret_cast<T&>(src.stack)));
  246. destroy(src);
  247. }
  248. static void swap(storage_union& lhs, storage_union& rhs) noexcept
  249. {
  250. storage_union tmp_storage;
  251. move(rhs, tmp_storage);
  252. move(lhs, rhs);
  253. move(tmp_storage, lhs);
  254. }
  255. };
  256. /// Whether the type T must be dynamically allocated or can be stored on the stack.
  257. template <typename T>
  258. struct requires_allocation : std::integral_constant<bool,
  259. !(std::is_nothrow_move_constructible<T>::value // N4562 �6.3/3 [any.class]
  260. && sizeof(T) <= sizeof(storage_union::stack) && std::alignment_of<T>::value <= std::alignment_of<storage_union::stack_storage_t>::value)>
  261. {
  262. };
  263. /// Returns the pointer to the vtable of the type T.
  264. template <typename T>
  265. static vtable_type* vtable_for_type()
  266. {
  267. using VTableType = typename std::conditional<requires_allocation<T>::value, vtable_dynamic<T>, vtable_stack<T>>::type;
  268. static vtable_type table = {
  269. VTableType::type, VTableType::destroy,
  270. VTableType::copy, VTableType::move,
  271. VTableType::swap,
  272. };
  273. return &table;
  274. }
  275. protected:
  276. template <typename T>
  277. friend const T* any_cast(const any* operand) noexcept;
  278. template <typename T>
  279. friend T* any_cast(any* operand) noexcept;
  280. /// Same effect as is_same(this->type(), t);
  281. bool is_typed(const std::type_info& t) const
  282. {
  283. return is_same(this->type(), t);
  284. }
  285. /// Checks if two type infos are the same.
  286. ///
  287. /// If ANY_IMPL_FAST_TYPE_INFO_COMPARE is defined, checks only the address of the
  288. /// type infos, otherwise does an actual comparision. Checking addresses is
  289. /// only a valid approach when there's no interaction with outside sources
  290. /// (other shared libraries and such).
  291. static bool is_same(const std::type_info& a, const std::type_info& b)
  292. {
  293. #ifdef ANY_IMPL_FAST_TYPE_INFO_COMPARE
  294. return &a == &b;
  295. #else
  296. return a == b;
  297. #endif
  298. }
  299. /// Casts (with no type_info checks) the storage pointer as const T*.
  300. template <typename T>
  301. const T* cast() const noexcept
  302. {
  303. return requires_allocation<typename std::decay<T>::type>::value ? reinterpret_cast<const T*>(storage.dynamic) : reinterpret_cast<const T*>(&storage.stack);
  304. }
  305. /// Casts (with no type_info checks) the storage pointer as T*.
  306. template <typename T>
  307. T* cast() noexcept
  308. {
  309. return requires_allocation<typename std::decay<T>::type>::value ? reinterpret_cast<T*>(storage.dynamic) : reinterpret_cast<T*>(&storage.stack);
  310. }
  311. private:
  312. storage_union storage; // on offset(0) so no padding for align
  313. vtable_type* vtable;
  314. /// Chooses between stack and dynamic allocation for the type decay_t<ValueType>,
  315. /// assigns the correct vtable, and constructs the object on our storage.
  316. template <typename ValueType>
  317. void construct(ValueType&& value)
  318. {
  319. using T = typename std::decay<ValueType>::type;
  320. this->vtable = vtable_for_type<T>();
  321. return xtl::mpl::static_if<requires_allocation<T>::value>([&](auto self)
  322. {
  323. self(*this).storage.dynamic = new T(std::forward<ValueType>(value));
  324. }, /*else*/ [&](auto self)
  325. {
  326. new (&self(*this).storage.stack) T(std::forward<ValueType>(value));
  327. });
  328. }
  329. };
  330. namespace detail
  331. {
  332. template <typename ValueType>
  333. inline ValueType any_cast_move_if_true(typename std::remove_reference<ValueType>::type* p, std::true_type)
  334. {
  335. return std::move(*p);
  336. }
  337. template <typename ValueType>
  338. inline ValueType any_cast_move_if_true(typename std::remove_reference<ValueType>::type* p, std::false_type)
  339. {
  340. return *p;
  341. }
  342. }
  343. /// Performs *any_cast<add_const_t<remove_reference_t<ValueType>>>(&operand), or throws bad_any_cast on failure.
  344. template <typename ValueType>
  345. inline ValueType any_cast(const any& operand)
  346. {
  347. auto p = any_cast<typename std::add_const<typename std::remove_reference<ValueType>::type>::type>(&operand);
  348. detail::check_any_cast(p);
  349. return *p;
  350. }
  351. /// Performs *any_cast<remove_reference_t<ValueType>>(&operand), or throws bad_any_cast on failure.
  352. template <typename ValueType>
  353. inline ValueType any_cast(any& operand)
  354. {
  355. auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand);
  356. detail::check_any_cast(p);
  357. return *p;
  358. }
  359. ///
  360. /// If ANY_IMPL_ANYCAST_MOVEABLE is not defined, does as N4562 specifies:
  361. /// Performs *any_cast<remove_reference_t<ValueType>>(&operand), or throws bad_any_cast on failure.
  362. ///
  363. /// If ANY_IMPL_ANYCAST_MOVEABLE is defined, does as LWG Defect 2509 specifies:
  364. /// If ValueType is MoveConstructible and isn't a lvalue reference, performs
  365. /// std::move(*any_cast<remove_reference_t<ValueType>>(&operand)), otherwise
  366. /// *any_cast<remove_reference_t<ValueType>>(&operand). Throws bad_any_cast on failure.
  367. ///
  368. template <typename ValueType>
  369. inline ValueType any_cast(any&& operand)
  370. {
  371. #ifdef ANY_IMPL_ANY_CAST_MOVEABLE
  372. // https://cplusplus.github.io/LWG/lwg-active.html#2509
  373. using can_move = std::integral_constant<bool,
  374. std::is_move_constructible<ValueType>::value && !std::is_lvalue_reference<ValueType>::value>;
  375. #else
  376. using can_move = std::false_type;
  377. #endif
  378. auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand);
  379. detail::check_any_cast(p);
  380. return detail::any_cast_move_if_true<ValueType>(p, can_move());
  381. }
  382. /// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object
  383. /// contained by operand, otherwise nullptr.
  384. template <typename T>
  385. inline const T* any_cast(const any* operand) noexcept
  386. {
  387. if (operand == nullptr || !operand->is_typed(typeid(T)))
  388. return nullptr;
  389. else
  390. return operand->cast<T>();
  391. }
  392. /// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object
  393. /// contained by operand, otherwise nullptr.
  394. template <typename T>
  395. inline T* any_cast(any* operand) noexcept
  396. {
  397. if (operand == nullptr || !operand->is_typed(typeid(T)))
  398. return nullptr;
  399. else
  400. return operand->cast<T>();
  401. }
  402. }
  403. namespace std
  404. {
  405. inline void swap(xtl::any& lhs, xtl::any& rhs) noexcept
  406. {
  407. lhs.swap(rhs);
  408. }
  409. }
  410. #endif