ROSE 0.11.145.192
Optional.h
1// WARNING: Changes to this file must be contributed back to Sawyer or else they will
2// be clobbered by the next update from Sawyer. The Sawyer repository is at
3// https://gitlab.com/charger7534/sawyer.git.
4
5
6
7
8#ifndef Sawyer_Optional_H
9#define Sawyer_Optional_H
10
11#include <Sawyer/Sawyer.h>
12#include <boost/type_traits/aligned_storage.hpp>
13#include <boost/type_traits/type_with_alignment.hpp>
14
15#include <stdexcept>
16
17namespace Sawyer {
18
36class Nothing { // final
37public:
38 Nothing() {}
39 template<class T> explicit Nothing(T) {}
40 template<class T> Nothing& operator=(T) { return *this; }
41 bool operator==(const Nothing&) const { return true; }
42 bool operator!=(const Nothing&) const { return false; }
43 bool operator>(const Nothing&) const { return false; }
44 bool operator>=(const Nothing&) const { return true; }
45 bool operator<(const Nothing&) const { return false; }
46 bool operator<=(const Nothing&) const { return true; }
47};
48
55template<typename T>
56class Optional {
57
58 // Done as a union to avoid aliasing warnings from GCC
59 union SAWYER_MAY_ALIAS MayAlias {
60 unsigned char data_[sizeof(T)];
61 BOOST_DEDUCED_TYPENAME boost::type_with_alignment<boost::alignment_of<T>::value >::type aligner_;
62 } mayAlias_;
63
64 bool isEmpty_;
65
66 void *address() { return &mayAlias_; }
67 const void*address() const { return &mayAlias_; }
68
69#ifdef SAWYER_HAVE_BOOST_SERIALIZATION
70private:
71 friend class boost::serialization::access;
72
73 template<class S>
74 void save(S &s, const unsigned /*version*/) const {
75 s <<BOOST_SERIALIZATION_NVP(isEmpty_);
76 if (!isEmpty_)
77 s <<boost::serialization::make_nvp("value", get());
78 }
79
80 template<class S>
81 void load(S &s, const unsigned /*version*/) {
82 *this = Nothing();
83 bool skip = false;
84 s >>boost::serialization::make_nvp("isEmpty_", skip);
85 if (!skip) {
86 *this = T();
87 s >>boost::serialization::make_nvp("value", get());
88 }
89 }
90
91 BOOST_SERIALIZATION_SPLIT_MEMBER();
92#endif
93
94#ifdef SAWYER_HAVE_CEREAL
95private:
96 friend class cereal::access;
97
98 template<class Archive>
99 void CEREAL_SAVE_FUNCTION_NAME(Archive &archive) const {
100 archive(cereal::make_nvp("isEmpty", isEmpty_));
101 if (!isEmpty_)
102 archive(cereal::make_nvp("value", get()));
103 }
104
105 template<class Archive>
106 void CEREAL_LOAD_FUNCTION_NAME(Archive &archive) {
107 *this = Nothing();
108 bool isEmpty = false;
109 archive(CEREAL_NVP(isEmpty));
110 if (!isEmpty) {
111 *this = T{};
112 archive(cereal::make_nvp("value", get()));
113 }
114 }
115#endif
116
117public:
119 typedef T Value;
120
124 Optional(): isEmpty_(true) {}
125
129 Optional(const Value &v): isEmpty_(false) { // implicit
130 new (address()) Value(v); // copy constructed in place
131 }
132
136 Optional(const Nothing&): isEmpty_(true) {}
137
142 Optional(const Optional &other) {
143 isEmpty_ = other.isEmpty_;
144 if (!isEmpty_) {
145 const Value &otherValue = *other;
146 new (address()) Value(otherValue);
147 }
148 }
149
154 if (!isEmpty_) {
155 Value &thisValue = **this;
156 thisValue.~Value();
157 }
158 }
159
164 Optional& operator=(const Value &value) {
165 if (isEmpty_) {
166 new (address()) Value(value);
167 } else {
168 Value &thisValue = **this;
169 thisValue = value;
170 }
171 isEmpty_ = false;
172 return *this;
173 }
174
180 if (!isEmpty_) {
181 Value &thisValue = **this;
182 thisValue.~Value();
183 }
184 isEmpty_ = true;
185 return *this;
186 }
187
194 Optional& operator=(const Optional &other) {
195 if (isEmpty_ && !other.isEmpty_) {
196 const Value &otherValue = *other;
197 new (address()) Value(otherValue);
198 } else if (!isEmpty_) {
199 if (other.isEmpty_) {
200 Value &thisValue = **this;
201 thisValue.~Value();
202 } else {
203 Value &thisValue = **this;
204 const Value &otherValue = *other;
205 thisValue = otherValue;
206 }
207 }
208 isEmpty_ = other.isEmpty_;
209 return *this;
210 }
211
213 void reset() {
214 *this = Nothing();
215 }
216
223 const Value& operator*() const {
224 return get();
225 }
227 return get();
228 }
229 const Value& get() const {
230 if (isEmpty_)
231 throw std::domain_error("dereferenced nothing");
232 return *reinterpret_cast<const Value*>(address());
233 }
235 if (isEmpty_)
236 throw std::domain_error("dereferenced nothing");
237 return *reinterpret_cast<Value*>(address());
238 }
247 const Value* operator->() const {
248 return &get();
249 }
251 return &get();
252 }
267 const Value& orElse(const Value &dflt) const {
268 return isEmpty_ ? dflt : **this;
269 }
270 const Value& orElse(Value &dflt) {
271 return isEmpty_ ? dflt : **this;
272 }
273 const Optional orElse(const Optional &other) const {
274 return isEmpty_ ? other : *this;
275 }
283 Optional andThen(const Value &value) const {
284 return isEmpty_ ? *this : Optional(value);
285 }
286 Optional andTHen(Value &value) const {
287 return isEmpty_ ? *this : Optional(value);
288 }
289 const Optional& andThen(const Optional &other) const {
290 return isEmpty_ ? *this : other;
291 }
305 Value orDefault() const {
306 return isEmpty_ ? Value() : **this;
307 }
308
330 template<class U>
331 bool assignTo(U &out) const {
332 if (isEmpty_) {
333 return false;
334 } else {
335 out = **this;
336 return true;
337 }
338 }
339
347 bool isEqual(const Optional &other) const {
348 return (isEmpty_ && other.isEmpty_) || (!isEmpty_ && !other.isEmpty_ && get()==other.get());
349 }
350 bool isEqual(const Value &other) const {
351 return !isEmpty_ && get()==other;
352 }
353 bool isEqual(const Nothing&) const {
354 return isEmpty_;
355 }
363 template <typename F>
364 auto
365 fmap(F&& f) -> Optional<decltype(f(get()))> {
366 if (isEmpty_)
369 }
370
371
372 // The following trickery is to allow things like "if (x)" to work but without having an implicit
373 // conversion to bool which would cause no end of other problems. This is fixed in C++11.
374private:
375 typedef void(Optional::*unspecified_bool)() const;
376 void this_type_does_not_support_comparisons() const {}
377public:
389 operator unspecified_bool() const {
390 return isEmpty_ ? 0 : &Optional::this_type_does_not_support_comparisons;
391 }
392};
393
394
395// These functions intentionally do not compile. They are to prevent comparisons and thus save users from making
396// mistakes like this:
397// Optional<int> x = 0;
398// int y = 1;
399// if (x == y) // won't compile
400// if (x && *x == y) // what they really meant
401// if (x.isEqual(y)) // another valid way to write it
402template<typename T, typename U>
403bool operator==(const Optional<T> &lhs, const U&) {
404 lhs.this_type_does_not_support_comparisons();
405 return false;
406}
407
408template<typename T, typename U>
409bool operator!=(const Optional<T> &lhs, const U&) {
410 lhs.this_type_does_not_support_comparisons();
411 return false;
412}
413
414} // namespace
415#endif
Represents no value.
Definition Optional.h:36
Holds a value or nothing.
Definition Optional.h:56
Optional & operator=(const Nothing &)
Nothing assignment.
Definition Optional.h:179
const Value & orElse(Value &dflt)
Obtain value or something else.
Definition Optional.h:270
const Value & operator*() const
Dereference to obtain value.
Definition Optional.h:223
auto fmap(F &&f) -> Optional< decltype(f(get()))>
Conditionally apply a functor f to the contents of an Optional.
Definition Optional.h:365
T Value
Type of stored value.
Definition Optional.h:119
const Optional & andThen(const Optional &other) const
If a value is present, return something else.
Definition Optional.h:289
const Value * operator->() const
Obtain a pointer to the value.
Definition Optional.h:247
Value orDefault() const
Obtain a value or a default.
Definition Optional.h:305
Optional(const Value &v)
Construct from value.
Definition Optional.h:129
Optional & operator=(const Value &value)
Value assignment.
Definition Optional.h:164
const Optional orElse(const Optional &other) const
Obtain value or something else.
Definition Optional.h:273
Optional & operator=(const Optional &other)
Optional assignment.
Definition Optional.h:194
Optional(const Nothing &)
Construct from nothing.
Definition Optional.h:136
Value * operator->()
Obtain a pointer to the value.
Definition Optional.h:250
~Optional()
Destructor.
Definition Optional.h:153
Optional andTHen(Value &value) const
If a value is present, return something else.
Definition Optional.h:286
bool isEqual(const Nothing &) const
Compare two values.
Definition Optional.h:353
Value & operator*()
Dereference to obtain value.
Definition Optional.h:226
void reset()
Reset as if default-constructed.
Definition Optional.h:213
const Value & orElse(const Value &dflt) const
Obtain value or something else.
Definition Optional.h:267
const Value & get() const
Dereference to obtain value.
Definition Optional.h:229
Optional()
Default constructs nothing.
Definition Optional.h:124
Value & get()
Dereference to obtain value.
Definition Optional.h:234
bool assignTo(U &out) const
Conditionally save a value.
Definition Optional.h:331
bool isEqual(const Optional &other) const
Compare two values.
Definition Optional.h:347
Optional(const Optional &other)
Copy constructor.
Definition Optional.h:142
Optional andThen(const Value &value) const
If a value is present, return something else.
Definition Optional.h:283
bool isEqual(const Value &other) const
Compare two values.
Definition Optional.h:350
Sawyer support library.