🛠️🐜 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.

353 lines
14 KiB

  1. #ifndef AL_OPTIONAL_H
  2. #define AL_OPTIONAL_H
  3. #include <initializer_list>
  4. #include <type_traits>
  5. #include <utility>
  6. #include "almalloc.h"
  7. namespace al {
  8. struct nullopt_t { };
  9. struct in_place_t { };
  10. constexpr nullopt_t nullopt{};
  11. constexpr in_place_t in_place{};
  12. #define NOEXCEPT_AS(...) noexcept(noexcept(__VA_ARGS__))
  13. namespace detail_ {
  14. /* Base storage struct for an optional. Defines a trivial destructor, for types
  15. * that can be trivially destructed.
  16. */
  17. template<typename T, bool = std::is_trivially_destructible<T>::value>
  18. struct optstore_base {
  19. bool mHasValue{false};
  20. union {
  21. char mDummy{};
  22. T mValue;
  23. };
  24. constexpr optstore_base() noexcept { }
  25. template<typename ...Args>
  26. constexpr explicit optstore_base(in_place_t, Args&& ...args)
  27. noexcept(std::is_nothrow_constructible<T, Args...>::value)
  28. : mHasValue{true}, mValue{std::forward<Args>(args)...}
  29. { }
  30. ~optstore_base() = default;
  31. };
  32. /* Specialization needing a non-trivial destructor. */
  33. template<typename T>
  34. struct optstore_base<T, false> {
  35. bool mHasValue{false};
  36. union {
  37. char mDummy{};
  38. T mValue;
  39. };
  40. constexpr optstore_base() noexcept { }
  41. template<typename ...Args>
  42. constexpr explicit optstore_base(in_place_t, Args&& ...args)
  43. noexcept(std::is_nothrow_constructible<T, Args...>::value)
  44. : mHasValue{true}, mValue{std::forward<Args>(args)...}
  45. { }
  46. ~optstore_base() { if(mHasValue) al::destroy_at(std::addressof(mValue)); }
  47. };
  48. /* Next level of storage, which defines helpers to construct and destruct the
  49. * stored object.
  50. */
  51. template<typename T>
  52. struct optstore_helper : public optstore_base<T> {
  53. using optstore_base<T>::optstore_base;
  54. template<typename... Args>
  55. constexpr void construct(Args&& ...args) noexcept(std::is_nothrow_constructible<T, Args...>::value)
  56. {
  57. al::construct_at(std::addressof(this->mValue), std::forward<Args>(args)...);
  58. this->mHasValue = true;
  59. }
  60. constexpr void reset() noexcept
  61. {
  62. if(this->mHasValue)
  63. al::destroy_at(std::addressof(this->mValue));
  64. this->mHasValue = false;
  65. }
  66. constexpr void assign(const optstore_helper &rhs)
  67. noexcept(std::is_nothrow_copy_constructible<T>::value
  68. && std::is_nothrow_copy_assignable<T>::value)
  69. {
  70. if(!rhs.mHasValue)
  71. this->reset();
  72. else if(this->mHasValue)
  73. this->mValue = rhs.mValue;
  74. else
  75. this->construct(rhs.mValue);
  76. }
  77. constexpr void assign(optstore_helper&& rhs)
  78. noexcept(std::is_nothrow_move_constructible<T>::value
  79. && std::is_nothrow_move_assignable<T>::value)
  80. {
  81. if(!rhs.mHasValue)
  82. this->reset();
  83. else if(this->mHasValue)
  84. this->mValue = std::move(rhs.mValue);
  85. else
  86. this->construct(std::move(rhs.mValue));
  87. }
  88. };
  89. /* Define copy and move constructors and assignment operators, which may or may
  90. * not be trivial.
  91. */
  92. template<typename T, bool trivial_copy = std::is_trivially_copy_constructible<T>::value,
  93. bool trivial_move = std::is_trivially_move_constructible<T>::value,
  94. /* Trivial assignment is dependent on trivial construction+destruction. */
  95. bool = trivial_copy && std::is_trivially_copy_assignable<T>::value
  96. && std::is_trivially_destructible<T>::value,
  97. bool = trivial_move && std::is_trivially_move_assignable<T>::value
  98. && std::is_trivially_destructible<T>::value>
  99. struct optional_storage;
  100. /* Some versions of GCC have issues with 'this' in the following noexcept(...)
  101. * statements, so this macro is a workaround.
  102. */
  103. #define _this std::declval<optional_storage*>()
  104. /* Completely trivial. */
  105. template<typename T>
  106. struct optional_storage<T, true, true, true, true> : public optstore_helper<T> {
  107. using optstore_helper<T>::optstore_helper;
  108. constexpr optional_storage() noexcept = default;
  109. constexpr optional_storage(const optional_storage&) = default;
  110. constexpr optional_storage(optional_storage&&) = default;
  111. constexpr optional_storage& operator=(const optional_storage&) = default;
  112. constexpr optional_storage& operator=(optional_storage&&) = default;
  113. };
  114. /* Non-trivial move assignment. */
  115. template<typename T>
  116. struct optional_storage<T, true, true, true, false> : public optstore_helper<T> {
  117. using optstore_helper<T>::optstore_helper;
  118. constexpr optional_storage() noexcept = default;
  119. constexpr optional_storage(const optional_storage&) = default;
  120. constexpr optional_storage(optional_storage&&) = default;
  121. constexpr optional_storage& operator=(const optional_storage&) = default;
  122. constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
  123. { this->assign(std::move(rhs)); return *this; }
  124. };
  125. /* Non-trivial move construction. */
  126. template<typename T>
  127. struct optional_storage<T, true, false, true, false> : public optstore_helper<T> {
  128. using optstore_helper<T>::optstore_helper;
  129. constexpr optional_storage() noexcept = default;
  130. constexpr optional_storage(const optional_storage&) = default;
  131. constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue)))
  132. { if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
  133. constexpr optional_storage& operator=(const optional_storage&) = default;
  134. constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
  135. { this->assign(std::move(rhs)); return *this; }
  136. };
  137. /* Non-trivial copy assignment. */
  138. template<typename T>
  139. struct optional_storage<T, true, true, false, true> : public optstore_helper<T> {
  140. using optstore_helper<T>::optstore_helper;
  141. constexpr optional_storage() noexcept = default;
  142. constexpr optional_storage(const optional_storage&) = default;
  143. constexpr optional_storage(optional_storage&&) = default;
  144. constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
  145. { this->assign(rhs); return *this; }
  146. constexpr optional_storage& operator=(optional_storage&&) = default;
  147. };
  148. /* Non-trivial copy construction. */
  149. template<typename T>
  150. struct optional_storage<T, false, true, false, true> : public optstore_helper<T> {
  151. using optstore_helper<T>::optstore_helper;
  152. constexpr optional_storage() noexcept = default;
  153. constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue))
  154. { if(rhs.mHasValue) this->construct(rhs.mValue); }
  155. constexpr optional_storage(optional_storage&&) = default;
  156. constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
  157. { this->assign(rhs); return *this; }
  158. constexpr optional_storage& operator=(optional_storage&&) = default;
  159. };
  160. /* Non-trivial assignment. */
  161. template<typename T>
  162. struct optional_storage<T, true, true, false, false> : public optstore_helper<T> {
  163. using optstore_helper<T>::optstore_helper;
  164. constexpr optional_storage() noexcept = default;
  165. constexpr optional_storage(const optional_storage&) = default;
  166. constexpr optional_storage(optional_storage&&) = default;
  167. constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
  168. { this->assign(rhs); return *this; }
  169. constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
  170. { this->assign(std::move(rhs)); return *this; }
  171. };
  172. /* Non-trivial assignment, non-trivial move construction. */
  173. template<typename T>
  174. struct optional_storage<T, true, false, false, false> : public optstore_helper<T> {
  175. using optstore_helper<T>::optstore_helper;
  176. constexpr optional_storage() noexcept = default;
  177. constexpr optional_storage(const optional_storage&) = default;
  178. constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue)))
  179. { if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
  180. constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
  181. { this->assign(rhs); return *this; }
  182. constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
  183. { this->assign(std::move(rhs)); return *this; }
  184. };
  185. /* Non-trivial assignment, non-trivial copy construction. */
  186. template<typename T>
  187. struct optional_storage<T, false, true, false, false> : public optstore_helper<T> {
  188. using optstore_helper<T>::optstore_helper;
  189. constexpr optional_storage() noexcept = default;
  190. constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue))
  191. { if(rhs.mHasValue) this->construct(rhs.mValue); }
  192. constexpr optional_storage(optional_storage&&) = default;
  193. constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
  194. { this->assign(rhs); return *this; }
  195. constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
  196. { this->assign(std::move(rhs)); return *this; }
  197. };
  198. /* Completely non-trivial. */
  199. template<typename T>
  200. struct optional_storage<T, false, false, false, false> : public optstore_helper<T> {
  201. using optstore_helper<T>::optstore_helper;
  202. constexpr optional_storage() noexcept = default;
  203. constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue))
  204. { if(rhs.mHasValue) this->construct(rhs.mValue); }
  205. constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue)))
  206. { if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
  207. constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
  208. { this->assign(rhs); return *this; }
  209. constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
  210. { this->assign(std::move(rhs)); return *this; }
  211. };
  212. #undef _this
  213. } // namespace detail_
  214. #define REQUIRES(...) std::enable_if_t<(__VA_ARGS__),bool> = true
  215. template<typename T>
  216. class optional {
  217. using storage_t = detail_::optional_storage<T>;
  218. storage_t mStore{};
  219. public:
  220. using value_type = T;
  221. constexpr optional() = default;
  222. constexpr optional(const optional&) = default;
  223. constexpr optional(optional&&) = default;
  224. constexpr optional(nullopt_t) noexcept { }
  225. template<typename ...Args>
  226. constexpr explicit optional(in_place_t, Args&& ...args)
  227. NOEXCEPT_AS(storage_t{al::in_place, std::forward<Args>(args)...})
  228. : mStore{al::in_place, std::forward<Args>(args)...}
  229. { }
  230. template<typename U, REQUIRES(std::is_constructible<T, U&&>::value
  231. && !std::is_same<std::decay_t<U>, al::in_place_t>::value
  232. && !std::is_same<std::decay_t<U>, optional<T>>::value
  233. && std::is_convertible<U&&, T>::value)>
  234. constexpr optional(U&& rhs) NOEXCEPT_AS(storage_t{al::in_place, std::forward<U>(rhs)})
  235. : mStore{al::in_place, std::forward<U>(rhs)}
  236. { }
  237. template<typename U, REQUIRES(std::is_constructible<T, U&&>::value
  238. && !std::is_same<std::decay_t<U>, al::in_place_t>::value
  239. && !std::is_same<std::decay_t<U>, optional<T>>::value
  240. && !std::is_convertible<U&&, T>::value)>
  241. constexpr explicit optional(U&& rhs) NOEXCEPT_AS(storage_t{al::in_place, std::forward<U>(rhs)})
  242. : mStore{al::in_place, std::forward<U>(rhs)}
  243. { }
  244. ~optional() = default;
  245. constexpr optional& operator=(const optional&) = default;
  246. constexpr optional& operator=(optional&&) = default;
  247. constexpr optional& operator=(nullopt_t) noexcept { mStore.reset(); return *this; }
  248. template<typename U=T>
  249. constexpr std::enable_if_t<std::is_constructible<T, U>::value
  250. && std::is_assignable<T&, U>::value
  251. && !std::is_same<std::decay_t<U>, optional<T>>::value
  252. && (!std::is_same<std::decay_t<U>, T>::value || !std::is_scalar<U>::value),
  253. optional&> operator=(U&& rhs)
  254. {
  255. if(mStore.mHasValue)
  256. mStore.mValue = std::forward<U>(rhs);
  257. else
  258. mStore.construct(std::forward<U>(rhs));
  259. return *this;
  260. }
  261. constexpr const T* operator->() const { return std::addressof(mStore.mValue); }
  262. constexpr T* operator->() { return std::addressof(mStore.mValue); }
  263. constexpr const T& operator*() const& { return mStore.mValue; }
  264. constexpr T& operator*() & { return mStore.mValue; }
  265. constexpr const T&& operator*() const&& { return std::move(mStore.mValue); }
  266. constexpr T&& operator*() && { return std::move(mStore.mValue); }
  267. constexpr explicit operator bool() const noexcept { return mStore.mHasValue; }
  268. constexpr bool has_value() const noexcept { return mStore.mHasValue; }
  269. constexpr T& value() & { return mStore.mValue; }
  270. constexpr const T& value() const& { return mStore.mValue; }
  271. constexpr T&& value() && { return std::move(mStore.mValue); }
  272. constexpr const T&& value() const&& { return std::move(mStore.mValue); }
  273. template<typename U>
  274. constexpr T value_or(U&& defval) const&
  275. { return bool{*this} ? **this : static_cast<T>(std::forward<U>(defval)); }
  276. template<typename U>
  277. constexpr T value_or(U&& defval) &&
  278. { return bool{*this} ? std::move(**this) : static_cast<T>(std::forward<U>(defval)); }
  279. template<typename ...Args>
  280. constexpr T& emplace(Args&& ...args)
  281. {
  282. mStore.reset();
  283. mStore.construct(std::forward<Args>(args)...);
  284. return mStore.mValue;
  285. }
  286. template<typename U, typename ...Args>
  287. constexpr std::enable_if_t<std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value,
  288. T&> emplace(std::initializer_list<U> il, Args&& ...args)
  289. {
  290. mStore.reset();
  291. mStore.construct(il, std::forward<Args>(args)...);
  292. return mStore.mValue;
  293. }
  294. constexpr void reset() noexcept { mStore.reset(); }
  295. };
  296. template<typename T>
  297. constexpr optional<std::decay_t<T>> make_optional(T&& arg)
  298. { return optional<std::decay_t<T>>{in_place, std::forward<T>(arg)}; }
  299. template<typename T, typename... Args>
  300. constexpr optional<T> make_optional(Args&& ...args)
  301. { return optional<T>{in_place, std::forward<Args>(args)...}; }
  302. template<typename T, typename U, typename... Args>
  303. constexpr optional<T> make_optional(std::initializer_list<U> il, Args&& ...args)
  304. { return optional<T>{in_place, il, std::forward<Args>(args)...}; }
  305. #undef REQUIRES
  306. #undef NOEXCEPT_AS
  307. } // namespace al
  308. #endif /* AL_OPTIONAL_H */