ROSE  0.10.7.0
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://github.com/matzke1/sawyer.
4 
5 
6 
7 
8 #ifndef Sawyer_Optional_H
9 #define Sawyer_Optional_H
10 
11 #include <Sawyer/Sawyer.h>
12 #include <boost/serialization/access.hpp>
13 #include <boost/serialization/nvp.hpp>
14 #include <boost/serialization/split_member.hpp>
15 #include <boost/type_traits/aligned_storage.hpp>
16 #include <boost/type_traits/type_with_alignment.hpp>
17 
18 #include <stdexcept>
19 
20 namespace Sawyer {
21 
32 class Nothing { // final
33 public:
34  bool operator==(const Nothing&) const { return true; }
35  bool operator!=(const Nothing&) const { return false; }
36  bool operator>(const Nothing&) const { return false; }
37  bool operator>=(const Nothing&) const { return true; }
38  bool operator<(const Nothing&) const { return false; }
39  bool operator<=(const Nothing&) const { return true; }
40 };
41 
48 template<typename T>
49 class Optional {
50 
51  // Done as a union to avoid aliasing warnings from GCC
52  union SAWYER_MAY_ALIAS MayAlias {
53  unsigned char data_[sizeof(T)];
54  BOOST_DEDUCED_TYPENAME boost::type_with_alignment<boost::alignment_of<T>::value >::type aligner_;
55  } mayAlias_;
56 
57  bool isEmpty_;
58 
59  void *address() { return &mayAlias_; }
60  const void*address() const { return &mayAlias_; }
61 
62 private:
63  friend class boost::serialization::access;
64 
65  template<class S>
66  void save(S &s, const unsigned /*version*/) const {
67  s <<BOOST_SERIALIZATION_NVP(isEmpty_);
68  if (!isEmpty_)
69  s <<boost::serialization::make_nvp("value", get());
70  }
71 
72  template<class S>
73  void load(S &s, const unsigned /*version*/) {
74  *this = Nothing();
75  bool skip = false;
76  s >>boost::serialization::make_nvp("isEmpty_", skip);
77  if (!skip) {
78  *this = T();
79  s >>boost::serialization::make_nvp("value", get());
80  }
81  }
82 
83  BOOST_SERIALIZATION_SPLIT_MEMBER();
84 
85 public:
87  typedef T Value;
88 
92  Optional(): isEmpty_(true) {}
93 
97  Optional(const Value &v): isEmpty_(false) { // implicit
98  new (address()) Value(v); // copy constructed in place
99  }
100 
104  Optional(const Nothing&): isEmpty_(true) {}
105 
110  Optional(const Optional &other) {
111  isEmpty_ = other.isEmpty_;
112  if (!isEmpty_) {
113  const Value &otherValue = *other;
114  new (address()) Value(otherValue);
115  }
116  }
117 
122  if (!isEmpty_) {
123  Value &thisValue = **this;
124  thisValue.~Value();
125  }
126  }
127 
132  Optional& operator=(const Value &value) {
133  if (isEmpty_) {
134  new (address()) Value(value);
135  } else {
136  Value &thisValue = **this;
137  thisValue = value;
138  }
139  isEmpty_ = false;
140  return *this;
141  }
142 
148  if (!isEmpty_) {
149  Value &thisValue = **this;
150  thisValue.~Value();
151  }
152  isEmpty_ = true;
153  return *this;
154  }
155 
162  Optional& operator=(const Optional &other) {
163  if (isEmpty_ && !other.isEmpty_) {
164  const Value &otherValue = *other;
165  new (address()) Value(otherValue);
166  } else if (!isEmpty_) {
167  if (other.isEmpty_) {
168  Value &thisValue = **this;
169  thisValue.~Value();
170  } else {
171  Value &thisValue = **this;
172  const Value &otherValue = *other;
173  thisValue = otherValue;
174  }
175  }
176  isEmpty_ = other.isEmpty_;
177  return *this;
178  }
179 
181  void reset() {
182  *this = Nothing();
183  }
184 
191  const Value& operator*() const {
192  return get();
193  }
194  Value& operator*() {
195  return get();
196  }
197  const Value& get() const {
198  if (isEmpty_)
199  throw std::domain_error("dereferenced nothing");
200  return *reinterpret_cast<const Value*>(address());
201  }
202  Value& get() {
203  if (isEmpty_)
204  throw std::domain_error("dereferenced nothing");
205  return *reinterpret_cast<Value*>(address());
206  }
215  const Value* operator->() const {
216  return &get();
217  }
218  Value* operator->() {
219  return &get();
220  }
235  const Value& orElse(const Value &dflt) const {
236  return isEmpty_ ? dflt : **this;
237  }
238  const Value& orElse(Value &dflt) {
239  return isEmpty_ ? dflt : **this;
240  }
254  Value orDefault() const {
255  return isEmpty_ ? Value() : **this;
256  }
257 
279  template<class U>
280  bool assignTo(U &out) const {
281  if (isEmpty_) {
282  return false;
283  } else {
284  out = **this;
285  return true;
286  }
287  }
288 
296  bool isEqual(const Optional &other) const {
297  return (isEmpty_ && other.isEmpty_) || (!isEmpty_ && !other.isEmpty_ && get()==other.get());
298  }
299  bool isEqual(const Value &other) const {
300  return !isEmpty_ && get()==other;
301  }
302  bool isEqual(const Nothing&) const {
303  return isEmpty_;
304  }
307  // The following trickery is to allow things like "if (x)" to work but without having an implicit
308  // conversion to bool which would cause no end of other problems. This is fixed in C++11.
309 private:
310  typedef void(Optional::*unspecified_bool)() const;
311  void this_type_does_not_support_comparisons() const {}
312 public:
324  operator unspecified_bool() const {
325  return isEmpty_ ? 0 : &Optional::this_type_does_not_support_comparisons;
326  }
327 };
328 
329 
330 // These functions intentionally do not compile. They are to prevent comparisons and thus save users from making
331 // mistakes like this:
332 // Optional<int> x = 0;
333 // int y = 1;
334 // if (x == y) // won't compile
335 // if (x && *x == y) // what they really meant
336 // if (x.isEqual(y)) // another valid way to write it
337 template<typename T, typename U>
338 bool operator==(const Optional<T> &lhs, const U &rhs) {
339  lhs.this_type_does_not_support_comparisons();
340  return false;
341 }
342 
343 template<typename T, typename U>
344 bool operator!=(const Optional<T> &lhs, const U &rhs) {
345  lhs.this_type_does_not_support_comparisons();
346  return false;
347 }
348 
349 } // namespace
350 #endif
Optional(const Value &v)
Construct from value.
Definition: Optional.h:97
Optional & operator=(const Nothing &)
Nothing assignment.
Definition: Optional.h:147
const Value & operator*() const
Dereference to obtain value.
Definition: Optional.h:191
Value orDefault() const
Obtain a value or a default.
Definition: Optional.h:254
Value & operator*()
Dereference to obtain value.
Definition: Optional.h:194
const Value * operator->() const
Obtain a pointer to the value.
Definition: Optional.h:215
T Value
Type of stored value.
Definition: Optional.h:87
Holds a value or nothing.
Definition: Optional.h:49
bool isEqual(const Optional &other) const
Compare two values.
Definition: Optional.h:296
Optional & operator=(const Optional &other)
Optional assignment.
Definition: Optional.h:162
const Value & orElse(Value &dflt)
Obtain value or something else.
Definition: Optional.h:238
void reset()
Reset as if default-constructed.
Definition: Optional.h:181
Optional()
Default constructs nothing.
Definition: Optional.h:92
bool isEqual(const Nothing &) const
Compare two values.
Definition: Optional.h:302
Name space for the entire library.
Optional(const Nothing &)
Construct from nothing.
Definition: Optional.h:104
Optional & operator=(const Value &value)
Value assignment.
Definition: Optional.h:132
~Optional()
Destructor.
Definition: Optional.h:121
Optional(const Optional &other)
Copy constructor.
Definition: Optional.h:110
bool assignTo(U &out) const
Conditionally save a value.
Definition: Optional.h:280
bool isEqual(const Value &other) const
Compare two values.
Definition: Optional.h:299
const Value & get() const
Dereference to obtain value.
Definition: Optional.h:197
Represents no value.
Definition: Optional.h:32
Value * operator->()
Obtain a pointer to the value.
Definition: Optional.h:218
const Value & orElse(const Value &dflt) const
Obtain value or something else.
Definition: Optional.h:235