optional.hpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. // Copyright (c) 2016-2021 Thomas Fussell
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. // THE SOFTWARE
  20. //
  21. // @license: http://www.opensource.org/licenses/mit-license.php
  22. // @author: see AUTHORS file
  23. #pragma once
  24. #include "xlnt/utils/exceptions.hpp"
  25. #include "xlnt/utils/numeric.hpp"
  26. #include "xlnt/xlnt_config.hpp"
  27. #include <type_traits>
  28. namespace xlnt {
  29. /// <summary>
  30. /// Many settings in xlnt are allowed to not have a value set. This class
  31. /// encapsulates a value which may or may not be set. Memory is allocated
  32. /// within the optional class.
  33. /// </summary>
  34. template <typename T>
  35. class optional
  36. {
  37. #if ((defined(_MSC_VER) && _MSC_VER <= 1900) || (defined(__GNUC__) && __GNUC__ < 5))
  38. // Disable enhanced type checking on Visual Studio <= 2015 and GCC <5
  39. #define XLNT_NOEXCEPT_VALUE_COMPAT(...) (false)
  40. #else
  41. #define XLNT_NOEXCEPT_VALUE_COMPAT(...) (__VA_ARGS__)
  42. using ctor_copy_T_noexcept = typename std::conditional<std::is_nothrow_copy_constructible<T>{}, std::true_type, std::false_type>::type;
  43. using ctor_move_T_noexcept = typename std::conditional<std::is_nothrow_move_constructible<T>{}, std::true_type, std::false_type>::type;
  44. using copy_ctor_noexcept = ctor_copy_T_noexcept;
  45. using move_ctor_noexcept = ctor_move_T_noexcept;
  46. using set_copy_noexcept_t = typename std::conditional<std::is_nothrow_copy_constructible<T>{} && std::is_nothrow_assignable<T, T>{}, std::true_type, std::false_type>::type;
  47. using set_move_noexcept_t = typename std::conditional<std::is_nothrow_move_constructible<T>{} && std::is_nothrow_move_assignable<T>{}, std::true_type, std::false_type>::type;
  48. using clear_noexcept_t = typename std::conditional<std::is_nothrow_destructible<T>{}, std::true_type, std::false_type>::type;
  49. #endif
  50. /// <summary>
  51. /// Default equality operation, just uses operator==
  52. /// </summary>
  53. template <typename U = T, typename std::enable_if<!std::is_floating_point<U>::value>::type * = nullptr>
  54. constexpr bool compare_equal(const U &lhs, const U &rhs) const
  55. {
  56. return lhs == rhs;
  57. }
  58. /// <summary>
  59. /// equality operation for floating point numbers. Provides "fuzzy" equality
  60. /// </summary>
  61. template <typename U = T, typename std::enable_if<std::is_floating_point<U>::value>::type * = nullptr>
  62. constexpr bool compare_equal(const U &lhs, const U &rhs) const
  63. {
  64. return detail::float_equals(lhs, rhs);
  65. }
  66. public:
  67. /// <summary>
  68. /// Default contructor. is_set() will be false initially.
  69. /// </summary>
  70. optional() noexcept
  71. : has_value_(false)
  72. {
  73. }
  74. /// <summary>
  75. /// Constructs this optional with a value.
  76. /// noexcept if T copy ctor is noexcept
  77. /// </summary>
  78. optional(const T &value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(ctor_copy_T_noexcept{}))
  79. : has_value_(true)
  80. {
  81. new (&storage_) T(value);
  82. }
  83. /// <summary>
  84. /// Constructs this optional with a value.
  85. /// noexcept if T move ctor is noexcept
  86. /// </summary>
  87. optional(T &&value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(ctor_move_T_noexcept{}))
  88. : has_value_(true)
  89. {
  90. new (&storage_) T(std::move(value));
  91. }
  92. /// <summary>
  93. /// Copy constructs this optional from other
  94. /// noexcept if T copy ctor is noexcept
  95. /// </summary>
  96. optional(const optional &other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(copy_ctor_noexcept{}))
  97. : has_value_(other.has_value_)
  98. {
  99. if (has_value_)
  100. {
  101. new (&storage_) T(other.value_ref());
  102. }
  103. }
  104. /// <summary>
  105. /// Move constructs this optional from other. Clears the value from other if set
  106. /// noexcept if T move ctor is noexcept
  107. /// </summary>
  108. optional(optional &&other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(move_ctor_noexcept{}))
  109. : has_value_(other.has_value_)
  110. {
  111. if (has_value_)
  112. {
  113. new (&storage_) T(std::move(other.value_ref()));
  114. other.clear();
  115. }
  116. }
  117. /// <summary>
  118. /// Copy assignment of this optional from other
  119. /// noexcept if set and clear are noexcept for T&
  120. /// </summary>
  121. optional &operator=(const optional &other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_copy_noexcept_t{} && clear_noexcept_t{}))
  122. {
  123. if (other.has_value_)
  124. {
  125. set(other.value_ref());
  126. }
  127. else
  128. {
  129. clear();
  130. }
  131. return *this;
  132. }
  133. /// <summary>
  134. /// Move assignment of this optional from other
  135. /// noexcept if set and clear are noexcept for T&&
  136. /// </summary>
  137. optional &operator=(optional &&other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_move_noexcept_t{} && clear_noexcept_t{}))
  138. {
  139. if (other.has_value_)
  140. {
  141. set(std::move(other.value_ref()));
  142. other.clear();
  143. }
  144. else
  145. {
  146. clear();
  147. }
  148. return *this;
  149. }
  150. /// <summary>
  151. /// Destructor cleans up the T instance if set
  152. /// </summary>
  153. ~optional() noexcept // note:: unconditional because msvc freaks out otherwise
  154. {
  155. clear();
  156. }
  157. /// <summary>
  158. /// Returns true if this object currently has a value set. This should
  159. /// be called before accessing the value with optional::get().
  160. /// </summary>
  161. bool is_set() const noexcept
  162. {
  163. return has_value_;
  164. }
  165. /// <summary>
  166. /// Copies the value into the stored value
  167. /// </summary>
  168. void set(const T &value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_copy_noexcept_t{}))
  169. {
  170. #if defined(__GNUC__) && !defined(__clang__)
  171. #pragma GCC diagnostic push
  172. #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
  173. #endif
  174. if (has_value_)
  175. {
  176. value_ref() = value;
  177. }
  178. else
  179. {
  180. new (&storage_) T(value);
  181. has_value_ = true;
  182. }
  183. #if defined(__GNUC__) && !defined(__clang__)
  184. #pragma GCC diagnostic pop
  185. #endif
  186. }
  187. /// <summary>
  188. /// Moves the value into the stored value
  189. /// </summary>
  190. void set(T &&value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_move_noexcept_t{}))
  191. {
  192. // note seperate overload for two reasons (as opposed to perfect forwarding)
  193. // 1. have to deal with implicit conversions internally with perfect forwarding
  194. // 2. have to deal with the noexcept specfiers for all the different variations
  195. // overload is just far and away the simpler solution
  196. #if defined(__GNUC__) && !defined(__clang__)
  197. #pragma GCC diagnostic push
  198. #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
  199. #endif
  200. if (has_value_)
  201. {
  202. value_ref() = std::move(value);
  203. }
  204. else
  205. {
  206. new (&storage_) T(std::move(value));
  207. has_value_ = true;
  208. }
  209. #if defined(__GNUC__) && !defined(__clang__)
  210. #pragma GCC diagnostic pop
  211. #endif
  212. }
  213. /// <summary>
  214. /// Assignment operator overload. Equivalent to setting the value using optional::set.
  215. /// </summary>
  216. optional &operator=(const T &rhs) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_copy_noexcept_t{}))
  217. {
  218. set(rhs);
  219. return *this;
  220. }
  221. /// <summary>
  222. /// Assignment operator overload. Equivalent to setting the value using optional::set.
  223. /// </summary>
  224. optional &operator=(T &&rhs) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_move_noexcept_t{}))
  225. {
  226. set(std::move(rhs));
  227. return *this;
  228. }
  229. /// <summary>
  230. /// After this is called, is_set() will return false until a new value is provided.
  231. /// </summary>
  232. void clear() noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(clear_noexcept_t{}))
  233. {
  234. if (has_value_)
  235. {
  236. reinterpret_cast<T *>(&storage_)->~T();
  237. }
  238. has_value_ = false;
  239. }
  240. /// <summary>
  241. /// Gets the value. If no value has been initialized in this object,
  242. /// an xlnt::invalid_attribute exception will be thrown.
  243. /// </summary>
  244. T &get()
  245. {
  246. if (!has_value_)
  247. {
  248. throw invalid_attribute();
  249. }
  250. return value_ref();
  251. }
  252. /// <summary>
  253. /// Gets the value. If no value has been initialized in this object,
  254. /// an xlnt::invalid_attribute exception will be thrown.
  255. /// </summary>
  256. const T &get() const
  257. {
  258. if (!has_value_)
  259. {
  260. throw invalid_attribute();
  261. }
  262. return value_ref();
  263. }
  264. /// <summary>
  265. /// Returns true if neither this nor other have a value
  266. /// or both have a value and those values are equal according to
  267. /// their equality operator.
  268. /// </summary>
  269. bool operator==(const optional<T> &other) const noexcept
  270. {
  271. if (has_value_ != other.has_value_)
  272. {
  273. return false;
  274. }
  275. if (!has_value_)
  276. {
  277. return true;
  278. }
  279. // equality is overloaded to provide fuzzy equality when T is a fp number
  280. return compare_equal(value_ref(), other.value_ref());
  281. }
  282. /// <summary>
  283. /// Returns false if neither this nor other have a value
  284. /// or both have a value and those values are equal according to
  285. /// their equality operator.
  286. /// </summary>
  287. bool operator!=(const optional<T> &other) const noexcept
  288. {
  289. return !operator==(other);
  290. }
  291. private:
  292. // helpers for getting a T out of storage
  293. T &value_ref() noexcept
  294. {
  295. return *reinterpret_cast<T *>(&storage_);
  296. }
  297. const T &value_ref() const noexcept
  298. {
  299. return *reinterpret_cast<const T *>(&storage_);
  300. }
  301. bool has_value_;
  302. typename std::aligned_storage<sizeof(T), alignof(T)>::type storage_;
  303. };
  304. #ifdef XLNT_NOEXCEPT_VALUE_COMPAT
  305. #undef XLNT_NOEXCEPT_VALUE_COMPAT
  306. #endif
  307. } // namespace xlnt