1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef LIB_FIT_OPTIONAL_H_
6 #define LIB_FIT_OPTIONAL_H_
7 
8 #include <assert.h>
9 
10 #include <new>
11 #include <type_traits>
12 #include <utility>
13 
14 namespace fit {
15 namespace internal {
16 
17 template <typename T, bool = std::is_assignable<T&, const T&>::value>
18 struct copy_assign_or_reconstruct final {
assignfinal19     static void assign(T* dest, const T& source) {
20         dest->~T();
21         new (dest) T(source);
22     }
23 };
24 
25 template <typename T>
26 struct copy_assign_or_reconstruct<T, true> final {
27     static void assign(T* dest, const T& source) {
28         *dest = source;
29     }
30 };
31 
32 template <typename T, bool = std::is_assignable<T&, T&&>::value>
33 struct move_assign_or_reconstruct final {
34     static void assign(T* dest, T&& source) {
35         dest->~T();
36         new (dest) T(std::move(source));
37     }
38 
39     static void swap(T& a, T& b) {
40         T temp(std::move(a));
41         a.~T();
42         new (&a) T(std::move(b));
43         b.~T();
44         new (&b) T(std::move(temp));
45     }
46 };
47 
48 template <typename T>
49 struct move_assign_or_reconstruct<T, true> final {
50     static void assign(T* dest, T&& source) {
51         *dest = std::move(source);
52     }
53 
54     static void swap(T& a, T& b) {
55         using std::swap;
56         swap(a, b);
57     }
58 };
59 
60 } // namespace internal
61 
62 // A sentinel value for |fit::optional<T>| indicating that it contains
63 // no value.
64 struct nullopt_t {
65     explicit constexpr nullopt_t(int) {}
66 };
67 static constexpr nullopt_t nullopt(0);
68 
69 // A minimal implementation of an |std::optional<T>| work-alike for C++ 14.
70 //
71 // See also |fit::nullable<T>| which may be more efficient in certain
72 // circumstances if T can be initialized, assigned, and compared with
73 // nullptr.
74 //
75 // TODO(US-90): The initial implementation only covers a minimal subset of the
76 // std::optional API.  Flesh this out more fully then define fit::optional
77 // to be an alias for std::optional when compiling with C++ 17.
78 template <typename T>
79 class optional final {
80 public:
81     using value_type = T;
82 
83     constexpr optional()
84         : has_value_(false) {}
85     constexpr optional(nullopt_t)
86         : has_value_(false) {}
87 
88     explicit constexpr optional(T value)
89         : has_value_(true), value_(std::move(value)) {}
90 
91     optional(const optional& other)
92         : has_value_(other.has_value_) {
93         if (has_value_) {
94             new (&value_) T(other.value_);
95         }
96     }
97 
98     optional(optional&& other)
99         : has_value_(other.has_value_) {
100         if (has_value_) {
101             new (&value_) T(std::move(other.value_));
102             other.value_.~T();
103             other.has_value_ = false;
104         }
105     }
106 
107     // TODO(US-90): Presence of this destructor makes the type non-literal.
108     // We should specialize this type to handle the case where T is literal
109     // explicitly so that expressions these types can be constexpr.
110     ~optional() {
111         if (has_value_) {
112             value_.~T();
113         }
114     }
115 
116     constexpr T& value() & {
117         assert(has_value_);
118         return value_;
119     }
120 
121     constexpr const T& value() const& {
122         assert(has_value_);
123         return value_;
124     }
125 
126     constexpr T&& value() && {
127         assert(has_value_);
128         return std::move(value_);
129     }
130 
131     constexpr const T&& value() const&& {
132         assert(has_value_);
133         return std::move(value_);
134     }
135 
136     template <typename U = T>
137     constexpr T value_or(U&& default_value) const {
138         return has_value_ ? value_ : static_cast<T>(std::forward<U>(default_value));
139     }
140 
141     constexpr T* operator->() { return &value_; }
142     constexpr const T* operator->() const { return &value_; }
143     constexpr T& operator*() { return value_; }
144     constexpr const T& operator*() const { return value_; }
145 
146     bool has_value() const { return has_value_; }
147     explicit operator bool() const { return has_value(); }
148 
149     optional& operator=(const optional& other) {
150         if (&other == this)
151             return *this;
152         if (has_value_) {
153             if (other.has_value_) {
154                 ::fit::internal::copy_assign_or_reconstruct<T>::assign(
155                     &value_, other.value_);
156             } else {
157                 reset();
158             }
159         } else if (other.has_value_) {
160             new (&value_) T(other.value_);
161             has_value_ = true;
162         }
163         return *this;
164     }
165 
166     optional& operator=(optional&& other) {
167         if (&other == this)
168             return *this;
169         if (has_value_) {
170             if (other.has_value_) {
171                 ::fit::internal::move_assign_or_reconstruct<T>::assign(
172                     &value_, std::move(other.value_));
173                 other.value_.~T();
174                 other.has_value_ = false;
175             } else {
176                 reset();
177             }
178         } else if (other.has_value_) {
179             new (&value_) T(std::move(other.value_));
180             has_value_ = true;
181             other.value_.~T();
182             other.has_value_ = false;
183         }
184         return *this;
185     }
186 
187     optional& operator=(nullopt_t) {
188         reset();
189         return *this;
190     }
191 
192     optional& operator=(T value) {
193         if (has_value_) {
194             ::fit::internal::move_assign_or_reconstruct<T>::assign(
195                 &value_, std::move(value));
196         } else {
197             new (&value_) T(std::move(value));
198             has_value_ = true;
199         }
200         return *this;
201     }
202 
203     void reset() {
204         if (has_value_) {
205             value_.~T();
206             has_value_ = false;
207         }
208     }
209 
210     void swap(optional& other) {
211         if (&other == this)
212             return;
213         if (has_value_) {
214             if (other.has_value_) {
215                 ::fit::internal::move_assign_or_reconstruct<T>::swap(
216                     value_, other.value_);
217             } else {
218                 new (&other.value_) T(std::move(value_));
219                 other.has_value_ = true;
220                 value_.~T();
221                 has_value_ = false;
222             }
223         } else if (other.has_value_) {
224             new (&value_) T(std::move(other.value_));
225             has_value_ = true;
226             other.value_.~T();
227             other.has_value_ = false;
228         }
229     }
230 
231     template <typename... Args>
232     T& emplace(Args&&... args) {
233         reset();
234         new (&value_) T(std::forward<Args...>(args)...);
235         has_value_ = true;
236         return value_;
237     }
238 
239 private:
240     bool has_value_;
241     union {
242         T value_;
243     };
244 };
245 
246 template <typename T>
247 void swap(optional<T>& a, optional<T>& b) {
248     a.swap(b);
249 }
250 
251 template <typename T>
252 constexpr bool operator==(const optional<T>& lhs, nullopt_t) {
253     return !lhs.has_value();
254 }
255 template <typename T>
256 constexpr bool operator!=(const optional<T>& lhs, nullopt_t) {
257     return lhs.has_value();
258 }
259 
260 template <typename T>
261 constexpr bool operator==(nullopt_t, const optional<T>& rhs) {
262     return !rhs.has_value();
263 }
264 template <typename T>
265 constexpr bool operator!=(nullopt_t, const optional<T>& rhs) {
266     return rhs.has_value();
267 }
268 
269 template <typename T, typename U>
270 constexpr bool operator==(const optional<T>& lhs, const optional<U>& rhs) {
271     return (lhs.has_value() == rhs.has_value()) && (!lhs.has_value() || *lhs == *rhs);
272 }
273 template <typename T, typename U>
274 constexpr bool operator!=(const optional<T>& lhs, const optional<U>& rhs) {
275     return (lhs.has_value() != rhs.has_value()) || (lhs.has_value() && *lhs != *rhs);
276 }
277 
278 template <typename T, typename U>
279 constexpr bool operator==(const optional<T>& lhs, const U& rhs) {
280     return lhs.has_value() && *lhs == rhs;
281 }
282 template <typename T, typename U>
283 constexpr bool operator!=(const optional<T>& lhs, const U& rhs) {
284     return !lhs.has_value() || *lhs != rhs;
285 }
286 
287 template <typename T, typename U>
288 constexpr bool operator==(const T& lhs, const optional<U>& rhs) {
289     return rhs.has_value() && lhs == *rhs;
290 }
291 template <typename T, typename U>
292 constexpr bool operator!=(const T& lhs, const optional<U>& rhs) {
293     return !rhs.has_value() || lhs != *rhs;
294 }
295 
296 } // namespace fit
297 
298 #endif // LIB_FIT_OPTIONAL_H_
299