1 // Copyright 2016 The Chromium 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 BASE_OPTIONAL_H_
6 #define BASE_OPTIONAL_H_
7 
8 #include <type_traits>
9 
10 #include "base/logging.h"
11 #include "base/memory/aligned_memory.h"
12 #include "base/template_util.h"
13 
14 namespace base {
15 
16 // Specification:
17 // http://en.cppreference.com/w/cpp/utility/optional/in_place_t
18 struct in_place_t {};
19 
20 // Specification:
21 // http://en.cppreference.com/w/cpp/utility/optional/nullopt_t
22 struct nullopt_t {
nullopt_tnullopt_t23   constexpr explicit nullopt_t(int) {}
24 };
25 
26 // Specification:
27 // http://en.cppreference.com/w/cpp/utility/optional/in_place
28 constexpr in_place_t in_place = {};
29 
30 // Specification:
31 // http://en.cppreference.com/w/cpp/utility/optional/nullopt
32 constexpr nullopt_t nullopt(0);
33 
34 namespace internal {
35 
36 template <typename T, bool = base::is_trivially_destructible<T>::value>
37 struct OptionalStorage {
38   // When T is not trivially destructible we must call its
39   // destructor before deallocating its memory.
~OptionalStorageOptionalStorage40   ~OptionalStorage() {
41     if (!is_null_)
42       buffer_.template data_as<T>()->~T();
43   }
44 
45   bool is_null_ = true;
46   base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_;
47 };
48 
49 template <typename T>
50 struct OptionalStorage<T, true> {
51   // When T is trivially destructible (i.e. its destructor does nothing)
52   // there is no need to call it.
53   // Since |base::AlignedMemory| is just an array its destructor
54   // is trivial. Explicitly defaulting the destructor means it's not
55   // user-provided. All of this together make this destructor trivial.
56   ~OptionalStorage() = default;
57 
58   bool is_null_ = true;
59   base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_;
60 };
61 
62 }  // namespace internal
63 
64 // base::Optional is a Chromium version of the C++17 optional class:
65 // std::optional documentation:
66 // http://en.cppreference.com/w/cpp/utility/optional
67 // Chromium documentation:
68 // https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md
69 //
70 // These are the differences between the specification and the implementation:
71 // - The constructor and emplace method using initializer_list are not
72 //   implemented because 'initializer_list' is banned from Chromium.
73 // - Constructors do not use 'constexpr' as it is a C++14 extension.
74 // - 'constexpr' might be missing in some places for reasons specified locally.
75 // - No exceptions are thrown, because they are banned from Chromium.
76 // - All the non-members are in the 'base' namespace instead of 'std'.
77 template <typename T>
78 class Optional {
79  public:
80   using value_type = T;
81 
82   constexpr Optional() = default;
83   Optional(base::nullopt_t) : Optional() {}
84 
85   Optional(const Optional& other) {
86     if (!other.storage_.is_null_)
87       Init(other.value());
88   }
89 
90   Optional(Optional&& other) {
91     if (!other.storage_.is_null_)
92       Init(std::move(other.value()));
93   }
94 
95   Optional(const T& value) { Init(value); }
96 
97   Optional(T&& value) { Init(std::move(value)); }
98 
99   template <class... Args>
100   explicit Optional(base::in_place_t, Args&&... args) {
101     emplace(std::forward<Args>(args)...);
102   }
103 
104   ~Optional() = default;
105 
106   Optional& operator=(base::nullopt_t) {
107     FreeIfNeeded();
108     return *this;
109   }
110 
111   Optional& operator=(const Optional& other) {
112     if (other.storage_.is_null_) {
113       FreeIfNeeded();
114       return *this;
115     }
116 
117     InitOrAssign(other.value());
118     return *this;
119   }
120 
121   Optional& operator=(Optional&& other) {
122     if (other.storage_.is_null_) {
123       FreeIfNeeded();
124       return *this;
125     }
126 
127     InitOrAssign(std::move(other.value()));
128     return *this;
129   }
130 
131   template <class U>
132   typename std::enable_if<std::is_same<std::decay<U>, T>::value,
133                           Optional&>::type
134   operator=(U&& value) {
135     InitOrAssign(std::forward<U>(value));
136     return *this;
137   }
138 
139   // TODO(mlamouri): can't use 'constexpr' with DCHECK.
140   const T* operator->() const {
141     DCHECK(!storage_.is_null_);
142     return &value();
143   }
144 
145   // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
146   // meant to be 'constexpr const'.
147   T* operator->() {
148     DCHECK(!storage_.is_null_);
149     return &value();
150   }
151 
152   constexpr const T& operator*() const& { return value(); }
153 
154   // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
155   // meant to be 'constexpr const'.
156   T& operator*() & { return value(); }
157 
158   constexpr const T&& operator*() const&& { return std::move(value()); }
159 
160   // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
161   // meant to be 'constexpr const'.
162   T&& operator*() && { return std::move(value()); }
163 
164   constexpr explicit operator bool() const { return !storage_.is_null_; }
165 
166   // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
167   // meant to be 'constexpr const'.
168   T& value() & {
169     DCHECK(!storage_.is_null_);
170     return *storage_.buffer_.template data_as<T>();
171   }
172 
173   // TODO(mlamouri): can't use 'constexpr' with DCHECK.
174   const T& value() const& {
175     DCHECK(!storage_.is_null_);
176     return *storage_.buffer_.template data_as<T>();
177   }
178 
179   // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
180   // meant to be 'constexpr const'.
181   T&& value() && {
182     DCHECK(!storage_.is_null_);
183     return std::move(*storage_.buffer_.template data_as<T>());
184   }
185 
186   // TODO(mlamouri): can't use 'constexpr' with DCHECK.
187   const T&& value() const&& {
188     DCHECK(!storage_.is_null_);
189     return std::move(*storage_.buffer_.template data_as<T>());
190   }
191 
192   template <class U>
193   constexpr T value_or(U&& default_value) const& {
194     // TODO(mlamouri): add the following assert when possible:
195     // static_assert(std::is_copy_constructible<T>::value,
196     //               "T must be copy constructible");
197     static_assert(std::is_convertible<U, T>::value,
198                   "U must be convertible to T");
199     return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
200                              : value();
201   }
202 
203   template <class U>
204   T value_or(U&& default_value) && {
205     // TODO(mlamouri): add the following assert when possible:
206     // static_assert(std::is_move_constructible<T>::value,
207     //               "T must be move constructible");
208     static_assert(std::is_convertible<U, T>::value,
209                   "U must be convertible to T");
210     return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
211                              : std::move(value());
212   }
213 
214   void swap(Optional& other) {
215     if (storage_.is_null_ && other.storage_.is_null_)
216       return;
217 
218     if (storage_.is_null_ != other.storage_.is_null_) {
219       if (storage_.is_null_) {
220         Init(std::move(*other.storage_.buffer_.template data_as<T>()));
221         other.FreeIfNeeded();
222       } else {
223         other.Init(std::move(*storage_.buffer_.template data_as<T>()));
224         FreeIfNeeded();
225       }
226       return;
227     }
228 
229     DCHECK(!storage_.is_null_ && !other.storage_.is_null_);
230     using std::swap;
231     swap(**this, *other);
232   }
233 
234   template <class... Args>
235   void emplace(Args&&... args) {
236     FreeIfNeeded();
237     Init(std::forward<Args>(args)...);
238   }
239 
240  private:
241   void Init(const T& value) {
242     DCHECK(storage_.is_null_);
243     new (storage_.buffer_.void_data()) T(value);
244     storage_.is_null_ = false;
245   }
246 
247   void Init(T&& value) {
248     DCHECK(storage_.is_null_);
249     new (storage_.buffer_.void_data()) T(std::move(value));
250     storage_.is_null_ = false;
251   }
252 
253   template <class... Args>
254   void Init(Args&&... args) {
255     DCHECK(storage_.is_null_);
256     new (storage_.buffer_.void_data()) T(std::forward<Args>(args)...);
257     storage_.is_null_ = false;
258   }
259 
260   void InitOrAssign(const T& value) {
261     if (storage_.is_null_)
262       Init(value);
263     else
264       *storage_.buffer_.template data_as<T>() = value;
265   }
266 
267   void InitOrAssign(T&& value) {
268     if (storage_.is_null_)
269       Init(std::move(value));
270     else
271       *storage_.buffer_.template data_as<T>() = std::move(value);
272   }
273 
274   void FreeIfNeeded() {
275     if (storage_.is_null_)
276       return;
277     storage_.buffer_.template data_as<T>()->~T();
278     storage_.is_null_ = true;
279   }
280 
281   internal::OptionalStorage<T> storage_;
282 };
283 
284 template <class T>
285 constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) {
286   return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs);
287 }
288 
289 template <class T>
290 constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) {
291   return !(lhs == rhs);
292 }
293 
294 template <class T>
295 constexpr bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) {
296   return rhs == nullopt ? false : (lhs == nullopt ? true : *lhs < *rhs);
297 }
298 
299 template <class T>
300 constexpr bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) {
301   return !(rhs < lhs);
302 }
303 
304 template <class T>
305 constexpr bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) {
306   return rhs < lhs;
307 }
308 
309 template <class T>
310 constexpr bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) {
311   return !(lhs < rhs);
312 }
313 
314 template <class T>
315 constexpr bool operator==(const Optional<T>& opt, base::nullopt_t) {
316   return !opt;
317 }
318 
319 template <class T>
320 constexpr bool operator==(base::nullopt_t, const Optional<T>& opt) {
321   return !opt;
322 }
323 
324 template <class T>
325 constexpr bool operator!=(const Optional<T>& opt, base::nullopt_t) {
326   return !!opt;
327 }
328 
329 template <class T>
330 constexpr bool operator!=(base::nullopt_t, const Optional<T>& opt) {
331   return !!opt;
332 }
333 
334 template <class T>
335 constexpr bool operator<(const Optional<T>& opt, base::nullopt_t) {
336   return false;
337 }
338 
339 template <class T>
340 constexpr bool operator<(base::nullopt_t, const Optional<T>& opt) {
341   return !!opt;
342 }
343 
344 template <class T>
345 constexpr bool operator<=(const Optional<T>& opt, base::nullopt_t) {
346   return !opt;
347 }
348 
349 template <class T>
350 constexpr bool operator<=(base::nullopt_t, const Optional<T>& opt) {
351   return true;
352 }
353 
354 template <class T>
355 constexpr bool operator>(const Optional<T>& opt, base::nullopt_t) {
356   return !!opt;
357 }
358 
359 template <class T>
360 constexpr bool operator>(base::nullopt_t, const Optional<T>& opt) {
361   return false;
362 }
363 
364 template <class T>
365 constexpr bool operator>=(const Optional<T>& opt, base::nullopt_t) {
366   return true;
367 }
368 
369 template <class T>
370 constexpr bool operator>=(base::nullopt_t, const Optional<T>& opt) {
371   return !opt;
372 }
373 
374 template <class T>
375 constexpr bool operator==(const Optional<T>& opt, const T& value) {
376   return opt != nullopt ? *opt == value : false;
377 }
378 
379 template <class T>
380 constexpr bool operator==(const T& value, const Optional<T>& opt) {
381   return opt == value;
382 }
383 
384 template <class T>
385 constexpr bool operator!=(const Optional<T>& opt, const T& value) {
386   return !(opt == value);
387 }
388 
389 template <class T>
390 constexpr bool operator!=(const T& value, const Optional<T>& opt) {
391   return !(opt == value);
392 }
393 
394 template <class T>
395 constexpr bool operator<(const Optional<T>& opt, const T& value) {
396   return opt != nullopt ? *opt < value : true;
397 }
398 
399 template <class T>
400 constexpr bool operator<(const T& value, const Optional<T>& opt) {
401   return opt != nullopt ? value < *opt : false;
402 }
403 
404 template <class T>
405 constexpr bool operator<=(const Optional<T>& opt, const T& value) {
406   return !(opt > value);
407 }
408 
409 template <class T>
410 constexpr bool operator<=(const T& value, const Optional<T>& opt) {
411   return !(value > opt);
412 }
413 
414 template <class T>
415 constexpr bool operator>(const Optional<T>& opt, const T& value) {
416   return value < opt;
417 }
418 
419 template <class T>
420 constexpr bool operator>(const T& value, const Optional<T>& opt) {
421   return opt < value;
422 }
423 
424 template <class T>
425 constexpr bool operator>=(const Optional<T>& opt, const T& value) {
426   return !(opt < value);
427 }
428 
429 template <class T>
430 constexpr bool operator>=(const T& value, const Optional<T>& opt) {
431   return !(value < opt);
432 }
433 
434 template <class T>
435 constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) {
436   return Optional<typename std::decay<T>::type>(std::forward<T>(value));
437 }
438 
439 template <class T>
440 void swap(Optional<T>& lhs, Optional<T>& rhs) {
441   lhs.swap(rhs);
442 }
443 
444 }  // namespace base
445 
446 namespace std {
447 
448 template <class T>
449 struct hash<base::Optional<T>> {
450   size_t operator()(const base::Optional<T>& opt) const {
451     return opt == base::nullopt ? 0 : std::hash<T>()(*opt);
452   }
453 };
454 
455 }  // namespace std
456 
457 #endif  // BASE_OPTIONAL_H_
458