Research portable Memory game | Исследовать портируемую игру Память
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

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