xlnt - community edition
optional.hpp
1 // Copyright (c) 2016-2022 Thomas Fussell
2 // Copyright (c) 2024-2026 xlnt-community
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE
21 //
22 // @license: http://www.opensource.org/licenses/mit-license.php
23 // @author: see AUTHORS file
24 
25 #pragma once
26 
27 #include "xlnt/utils/exceptions.hpp"
28 #include <type_traits>
29 #include <cstring>
30 
31 namespace xlnt {
32 
38 template <typename T>
39 class optional
40 {
41 #if ((defined(_MSC_VER) && _MSC_VER <= 1900) || (defined(__GNUC__) && __GNUC__ < 5))
42 // Disable enhanced type checking on Visual Studio <= 2015 and GCC <5
43 #define XLNT_NOEXCEPT_VALUE_COMPAT(...) (false)
44 #else
45 #define XLNT_NOEXCEPT_VALUE_COMPAT(...) (__VA_ARGS__)
46  using ctor_copy_T_noexcept = typename std::conditional<std::is_nothrow_copy_constructible<T>{}, std::true_type, std::false_type>::type;
47  using ctor_move_T_noexcept = typename std::conditional<std::is_nothrow_move_constructible<T>{}, std::true_type, std::false_type>::type;
48  using copy_ctor_noexcept = ctor_copy_T_noexcept;
49  using move_ctor_noexcept = ctor_move_T_noexcept;
50  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;
51  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;
52  using clear_noexcept_t = typename std::conditional<std::is_nothrow_destructible<T>{}, std::true_type, std::false_type>::type;
53 #endif
54 
55 public:
59  optional() noexcept
60  : has_value_(false),
61  storage_{}
62  {
63  }
64 
69  optional(const T &value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(ctor_copy_T_noexcept{}))
70  : has_value_(true)
71  {
72  new (&storage_) T(value);
73  }
74 
79  optional(T &&value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(ctor_move_T_noexcept{}))
80  : has_value_(true)
81  {
82  new (&storage_) T(std::move(value));
83  }
84 
89  optional(const optional &other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(copy_ctor_noexcept{}))
90  : has_value_(other.has_value_)
91  {
92  if (has_value_)
93  {
94  new (&storage_) T(other.value_ref());
95  }
96  else
97  {
98  std::memset(storage_, 0, sizeof(T));
99  }
100  }
101 
106  optional(optional &&other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(move_ctor_noexcept{}))
107  : has_value_(other.has_value_)
108  {
109  if (has_value_)
110  {
111  new (&storage_) T(std::move(other.value_ref()));
112  other.clear();
113  }
114  else
115  {
116  std::memset(storage_, 0, sizeof(T));
117  }
118  }
119 
124  optional &operator=(const optional &other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_copy_noexcept_t{} && clear_noexcept_t{}))
125  {
126  if (other.has_value_)
127  {
128  set(other.value_ref());
129  }
130  else
131  {
132  clear();
133  }
134  return *this;
135  }
136 
141  optional &operator=(optional &&other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_move_noexcept_t{} && clear_noexcept_t{}))
142  {
143  if (other.has_value_)
144  {
145  set(std::move(other.value_ref()));
146  other.clear();
147  }
148  else
149  {
150  clear();
151  }
152  return *this;
153  }
154 
158  ~optional() noexcept // note:: unconditional because msvc freaks out otherwise
159  {
160  clear();
161  }
162 
167  bool is_set() const noexcept
168  {
169  return has_value_;
170  }
171 
175  void set(const T &value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_copy_noexcept_t{}))
176  {
177  if (has_value_)
178  {
179  value_ref() = value;
180  }
181  else
182  {
183  new (&storage_) T(value);
184  has_value_ = true;
185  }
186  }
187 
191  void set(T &&value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_move_noexcept_t{}))
192  {
193  // note seperate overload for two reasons (as opposed to perfect forwarding)
194  // 1. have to deal with implicit conversions internally with perfect forwarding
195  // 2. have to deal with the noexcept specfiers for all the different variations
196  // overload is just far and away the simpler solution
197  if (has_value_)
198  {
199  value_ref() = std::move(value);
200  }
201  else
202  {
203  new (&storage_) T(std::move(value));
204  has_value_ = true;
205  }
206  }
207 
211  optional &operator=(const T &rhs) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_copy_noexcept_t{}))
212  {
213  set(rhs);
214  return *this;
215  }
216 
220  optional &operator=(T &&rhs) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_move_noexcept_t{}))
221  {
222  set(std::move(rhs));
223  return *this;
224  }
225 
229  void clear() noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(clear_noexcept_t{}))
230  {
231  if (has_value_)
232  {
233  reinterpret_cast<T *>(&storage_)->~T();
234  }
235  has_value_ = false;
236  }
237 
243  T &get()
244  {
245  if (!has_value_)
246  {
247  throw invalid_attribute("access to empty optional");
248  }
249 
250  return value_ref();
251  }
252 
258  const T &get() const
259  {
260  if (!has_value_)
261  {
262  throw invalid_attribute("access to empty optional");
263  }
264 
265  return value_ref();
266  }
267 
273  bool operator==(const optional<T> &other) const noexcept
274  {
275  if (has_value_ != other.has_value_)
276  {
277  return false;
278  }
279  if (!has_value_)
280  {
281  return true;
282  }
283  return value_ref() == other.value_ref();
284  }
285 
291  bool operator!=(const optional<T> &other) const noexcept
292  {
293  return !operator==(other);
294  }
295 
296 private:
297  // helpers for getting a T out of storage
298  T &value_ref() noexcept
299  {
300  return *reinterpret_cast<T *>(&storage_);
301  }
302 
303  const T &value_ref() const noexcept
304  {
305  return *reinterpret_cast<const T *>(&storage_);
306  }
307 
308  bool has_value_;
309  alignas(T) unsigned char storage_[sizeof(T)];
310 };
311 
312 #ifdef XLNT_NOEXCEPT_VALUE_COMPAT
313 #undef XLNT_NOEXCEPT_VALUE_COMPAT
314 #endif
315 
316 } // namespace xlnt
optional() noexcept
Default contructor. is_set() will be false initially.
Definition: optional.hpp:59
void clear() noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(clear_noexcept_t{}))
After this is called, is_set() will return false until a new value is provided.
Definition: optional.hpp:229
bool is_set() const noexcept
Returns true if this object currently has a value set. This should be called before accessing the val...
Definition: optional.hpp:167
optional & operator=(T &&rhs) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_move_noexcept_t{}))
Assignment operator overload. Equivalent to setting the value using optional::set.
Definition: optional.hpp:220
optional(optional &&other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(move_ctor_noexcept{}))
Move constructs this optional from other. Clears the value from other if set noexcept if T move ctor ...
Definition: optional.hpp:106
bool operator==(const optional< T > &other) const noexcept
Returns true if neither this nor other have a value or both have a value and those values are equal a...
Definition: optional.hpp:273
optional(T &&value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(ctor_move_T_noexcept{}))
Constructs this optional with a value. noexcept if T move ctor is noexcept
Definition: optional.hpp:79
Enumerates the possible types a cell can be determined by it&#39;s current value.
Definition: cell.hpp:36
Exception when getting a class&#39;s attribute before being set/initialized, or when setting a class&#39;s at...
Definition: exceptions.hpp:171
Many settings in xlnt are allowed to not have a value set. This class encapsulates a value which may ...
Definition: format.hpp:44
optional & operator=(const T &rhs) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_copy_noexcept_t{}))
Assignment operator overload. Equivalent to setting the value using optional::set.
Definition: optional.hpp:211
~optional() noexcept
Destructor cleans up the T instance if set
Definition: optional.hpp:158
optional & operator=(const optional &other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_copy_noexcept_t{} &&clear_noexcept_t{}))
Copy assignment of this optional from other noexcept if set and clear are noexcept for T& ...
Definition: optional.hpp:124
optional(const T &value) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(ctor_copy_T_noexcept{}))
Constructs this optional with a value. noexcept if T copy ctor is noexcept
Definition: optional.hpp:69
optional(const optional &other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(copy_ctor_noexcept{}))
Copy constructs this optional from other noexcept if T copy ctor is noexcept
Definition: optional.hpp:89
optional & operator=(optional &&other) noexcept(XLNT_NOEXCEPT_VALUE_COMPAT(set_move_noexcept_t{} &&clear_noexcept_t{}))
Move assignment of this optional from other noexcept if set and clear are noexcept for T&& ...
Definition: optional.hpp:141
bool operator!=(const optional< T > &other) const noexcept
Returns false if neither this nor other have a value or both have a value and those values are equal ...
Definition: optional.hpp:291