Research portable Memory game | Исследовать портируемую игру Память
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.

550 lines
15KB

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