optional.hpp
1 #pragma once
2 
14 #include <exception>
15 #include <new>
16 #include <utility>
17 
18 #include "../namespace.hpp"
19 #include "utilities.hpp"
20 
21 namespace Cvb
22 {
23 
24 CVB_BEGIN_INLINE_NS
25 
26 namespace Shims
27 {
28 
33 {
34 public:
36  bad_optional_access() = default;
37 };
38 
47 struct nullopt_t
48 {
49 };
50 
52 static constexpr nullopt_t nullopt = {};
53 
58 template <class T>
59 class optional final
60 {
61  using Storage = typename std::aligned_storage<sizeof(T), alignof(T)>::type;
62 
64  bool has_value_;
66  Storage value_;
67 
68 public:
69  using value_type = T;
70 
72  optional() noexcept
73  : has_value_(false)
74  {
75  }
76 
78  optional(nullopt_t) noexcept
79  : has_value_(false)
80  {
81  }
82 
89  template <class... ARGS>
90  optional(in_place_t, ARGS &&... args)
91  : has_value_(true)
92  {
93  new(&value_) T(std::forward<ARGS>(args)...);
94  }
95 
102  template <class U, typename std::enable_if<std::is_assignable<T&, U>::value, int>::type = 0>
104  : has_value_(true)
105  {
106  new(&value_) T(std::forward<U>(value));
107  }
108 
116  template <class U, typename std::enable_if<std::is_assignable<T&, U>::value, int>::type = 0>
118  {
119  if(this->has_value_)
120  {
121  reinterpret_cast<T &>(value_).operator=(std::forward<U>(value));
122  }
123  else
124  {
125  new(&value_) T(std::forward<U>(value));
126  has_value_ = true;
127  }
128 
129  return *this;
130  }
131 
137  template <typename std::enable_if<std::is_copy_constructible<T>::value, int>::type = 0>
138  optional(const optional &rhs)
139  : has_value_(rhs.has_value_)
140  {
141  if(rhs.has_value_)
142  {
143  new(&value_) T(*rhs);
144  }
145  }
146 
153  template <typename std::enable_if<std::is_copy_assignable<T>::value, int>::type = 0>
155  {
156  if(this != &rhs)
157  {
158  if(has_value_)
159  {
160  if(rhs.has_value_)
161  reinterpret_cast<T &>(value_).operator=(*rhs);
162  else
163  this->reset();
164  }
165  else if(rhs.has_value_)
166  {
167  new(&value_) T(*rhs);
168  has_value_ = true;
169  }
170  }
171 
172  return *this;
173  }
174 
180  template <typename std::enable_if<std::is_move_constructible<T>::value, int>::type = 0>
182  : has_value_(rhs.has_value_)
183  {
184  if(rhs.has_value_)
185  {
186  new(&value_) T(std::move(*rhs));
187  rhs.reset();
188  }
189  }
190 
197  template <typename std::enable_if<std::is_move_assignable<T>::value, int>::type = 0>
199  {
200  if(this != &rhs)
201  {
202  if(has_value_)
203  {
204  if(rhs.has_value_)
205  {
206  reinterpret_cast<T &>(value_).operator=(std::move(*rhs));
207  rhs.reset();
208  }
209  else
210  {
211  this->reset();
212  }
213  }
214  else if(rhs.has_value_)
215  {
216  new(&value_) T(std::move(*rhs));
217  has_value_ = true;
218  rhs.reset();
219  }
220  }
221 
222  return *this;
223  }
224 
227  {
228  this->reset();
229  }
230 
239  const T *operator->() const noexcept
240  {
241  return &reinterpret_cast<const T &>(value_);
242  }
243 
252  T *operator->() noexcept
253  {
254  return &reinterpret_cast<T &>(value_);
255  }
256 
265  const T &operator*() const noexcept
266  {
267  return reinterpret_cast<const T &>(value_);
268  }
269 
278  T &operator*() noexcept
279  {
280  return reinterpret_cast<T &>(value_);
281  }
282 
288  explicit operator bool() const noexcept
289  {
290  return has_value_;
291  }
292 
303  template <class... ARGS>
304  T &emplace(ARGS &&... args)
305  {
306  this->reset();
307  auto pValue = new(&value_) T(std::forward<ARGS>(args)...);
308  has_value_ = true;
309  return *pValue;
310  }
311 
317  bool has_value() const noexcept
318  {
319  return has_value_;
320  }
321 
325  void reset()
326  {
327  if(has_value_)
328  {
329  reinterpret_cast<T &>(value_).~T();
330  has_value_ = false;
331  }
332  }
333 
341  void swap(optional &other)
342  {
343  auto old = std::move(*this);
344  *this = std::move(other);
345  other = std::move(old);
346  }
347 
354  const T &value() const
355  {
356  if(!has_value_)
357  throw bad_optional_access{};
358 
359  return reinterpret_cast<const T &>(value_);
360  }
361 
368  T &value()
369  {
370  if(!has_value_)
371  throw bad_optional_access{};
372 
373  return reinterpret_cast<T &>(value_);
374  }
375 
382  template <class U, typename std::enable_if<std::is_assignable<T&, U>::value, int>::type = 0>
383  T value_or(U &&default_value) const
384  {
385  if(has_value_)
386  return reinterpret_cast<const T &>(value_);
387  else
388  return default_value;
389  }
390 };
391 
400 template <class T>
402 {
403  return optional<typename std::decay<T>::type>{std::forward<T>(value)};
404 }
405 
415 template <class T, class... ARGS>
416 optional<T> make_optional(ARGS &&... args)
417 {
418  return optional<T>{in_place, std::forward<ARGS>(args)...};
419 }
420 
421 } // namespace Shims
422 
423 CVB_END_INLINE_NS
424 
425 } // namespace Cvb
426 
427 namespace std
428 {
430 template <class T>
432 {
433  lhs.swap(rhs);
434 }
435 } // namespace std
This class is a replacement for C++17 std::optional.
Definition: optional.hpp:59
T * operator->() noexcept
Gets the reference to the stored object.
Definition: optional.hpp:252
optional(U &&value)
Ctor from an object assignable to T.
Definition: optional.hpp:103
optional(optional &&rhs)
Move ctor.
Definition: optional.hpp:181
bool has_value() const noexcept
Gets whether this optional contains a value.
Definition: optional.hpp:317
const T & operator *() const noexcept
Gets the reference to the stored object.
Definition: optional.hpp:265
T swap(T... args)
optional & operator=(const optional &rhs)
Copy operator.
Definition: optional.hpp:154
void swap(optional &other)
Swaps this object with the other object.
Definition: optional.hpp:341
optional< typename std::decay< T >::type > make_optional(T &&value)
Factory function to create an optional from a T object.
Definition: optional.hpp:401
optional< T > make_optional(ARGS &&... args)
Factory function to in-place construct a T in an optional.
Definition: optional.hpp:416
optional(const optional &rhs)
Copy ctor.
Definition: optional.hpp:138
Disambiguation tags that can be passed to the constructors of optional() to indicate that the contain...
Definition: utilities.hpp:23
Root namespace for the Image Manager interface.
Definition: version.hpp:11
T & emplace(ARGS &&... args)
Constructs a new T in-place.
Definition: optional.hpp:304
optional(in_place_t, ARGS &&... args)
In-place constructs a new T object.
Definition: optional.hpp:90
This class is a replacement for C++17 std::optional.
~optional()
Dtor.
Definition: optional.hpp:226
Error when accessing not set value.
Definition: optional.hpp:32
static constexpr nullopt_t nullopt
Tag to indicate an optional not containing a value.
Definition: optional.hpp:52
T & value()
Gets the contained value.
Definition: optional.hpp:368
optional & operator=(U &&value)
Assignment operator for an object assignable to T.
Definition: optional.hpp:117
optional(nullopt_t) noexcept
Ctor for explicitly not storing a value.
Definition: optional.hpp:78
T value_or(U &&default_value) const
Gets the contained value if has_value() is true; default_value otherwise.
Definition: optional.hpp:383
optional() noexcept
Default ctor not storing a value.
Definition: optional.hpp:72
STL class.
const T * operator->() const noexcept
Gets the reference to the stored object.
Definition: optional.hpp:239
void reset()
Destroys any contained value.
Definition: optional.hpp:325
const T & value() const
Gets the contained value.
Definition: optional.hpp:354
bad_optional_access()=default
Default ctor.
optional & operator=(optional &&rhs)
Move operator.
Definition: optional.hpp:198
Indicates optional() type with uninitialized state.
Definition: optional.hpp:47
static constexpr in_place_t in_place
Tag to indicate an optional to in-place construct a value.
Definition: utilities.hpp:28