ROSE  0.9.9.109
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 
186  const Value& operator*() const {
187  return get();
188  }
189  Value& operator*() {
190  return get();
191  }
192  const Value& get() const {
193  if (isEmpty_)
194  throw std::domain_error("dereferenced nothing");
195  return *reinterpret_cast<const Value*>(address());
196  }
197  Value& get() {
198  if (isEmpty_)
199  throw std::domain_error("dereferenced nothing");
200  return *reinterpret_cast<Value*>(address());
201  }
210  const Value* operator->() const {
211  return &get();
212  }
213  Value* operator->() {
214  return &get();
215  }
230  const Value& orElse(const Value &dflt) const {
231  return isEmpty_ ? dflt : **this;
232  }
233  const Value& orElse(Value &dflt) {
234  return isEmpty_ ? dflt : **this;
235  }
249  Value orDefault() const {
250  return isEmpty_ ? Value() : **this;
251  }
252 
274  bool assignTo(Value &out) const {
275  if (isEmpty_) {
276  return false;
277  } else {
278  out = **this;
279  return true;
280  }
281  }
282 
290  bool isEqual(const Optional &other) const {
291  return (isEmpty_ && other.isEmpty_) || (!isEmpty_ && !other.isEmpty_ && get()==other.get());
292  }
293  bool isEqual(const Value &other) const {
294  return !isEmpty_ && get()==other;
295  }
296  bool isEqual(const Nothing&) const {
297  return isEmpty_;
298  }
301  // The following trickery is to allow things like "if (x)" to work but without having an implicit
302  // conversion to bool which would cause no end of other problems. This is fixed in C++11.
303 private:
304  typedef void(Optional::*unspecified_bool)() const;
305  void this_type_does_not_support_comparisons() const {}
306 public:
318  operator unspecified_bool() const {
319  return isEmpty_ ? 0 : &Optional::this_type_does_not_support_comparisons;
320  }
321 };
322 
323 
324 // These functions intentionally do not compile. They are to prevent comparisons and thus save users from making
325 // mistakes like this:
326 // Optional<int> x = 0;
327 // int y = 1;
328 // if (x == y) // won't compile
329 // if (x && *x == y) // what they really meant
330 // if (x.isEqual(y)) // another valid way to write it
331 template<typename T, typename U>
332 bool operator==(const Optional<T> &lhs, const U &rhs) {
333  lhs.this_type_does_not_support_comparisons();
334  return false;
335 }
336 
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 } // namespace
344 #endif
bool assignTo(Value &out) const
Conditionally save a value.
Definition: Optional.h:274
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:186
Value orDefault() const
Obtain a value or a default.
Definition: Optional.h:249
Value & operator*()
Dereference to obtain value.
Definition: Optional.h:189
const Value * operator->() const
Obtain a pointer to the value.
Definition: Optional.h:210
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:290
Optional & operator=(const Optional &other)
Optional assignment.
Definition: Optional.h:162
const Value & orElse(Value &dflt)
Obtain value or something else.
Definition: Optional.h:233
Optional()
Default constructs nothing.
Definition: Optional.h:92
bool isEqual(const Nothing &) const
Compare two values.
Definition: Optional.h:296
Name space for the entire library.
Definition: Access.h:11
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 isEqual(const Value &other) const
Compare two values.
Definition: Optional.h:293
const Value & get() const
Dereference to obtain value.
Definition: Optional.h:192
Represents no value.
Definition: Optional.h:32
Value * operator->()
Obtain a pointer to the value.
Definition: Optional.h:213
const Value & orElse(const Value &dflt) const
Obtain value or something else.
Definition: Optional.h:230