• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_STDCOMPAT_INCLUDE_LIB_STDCOMPAT_OPTIONAL_H_
6 #define LIB_STDCOMPAT_INCLUDE_LIB_STDCOMPAT_OPTIONAL_H_
7 
8 #include "utility.h"
9 #include "version.h"
10 
11 #if defined(__cpp_lib_optional) && __cpp_lib_optional >= 201606L && \
12     !defined(LIB_STDCOMPAT_USE_POLYFILLS)
13 
14 #include <optional>
15 
16 namespace cpp17 {
17 
18 using std::bad_optional_access;
19 using std::make_optional;
20 using std::nullopt;
21 using std::nullopt_t;
22 using std::optional;
23 
24 }  // namespace cpp17
25 
26 #else  // Provide std::optional and std::nullopt_t polyfill.
27 
28 #include <cstdlib>
29 #include <exception>
30 #include <new>
31 
32 #include "internal/constructors.h"
33 #include "internal/exception.h"
34 #include "internal/storage.h"
35 #include "internal/utility.h"
36 #include "type_traits.h"
37 
38 namespace cpp17 {
39 
40 // A sentinel value for indicating that it contains no value.
41 struct nullopt_t {
nullopt_tnullopt_t42   explicit constexpr nullopt_t(int) {}
43 };
44 static constexpr nullopt_t nullopt{0};
45 
46 // Exception type to report bad accesses to optional.
47 class bad_optional_access : public std::exception {
48  public:
bad_optional_access()49   bad_optional_access() noexcept {}
50 
what()51   const char* what() const noexcept override { return reason_; }
52 
53  private:
54   template <typename T>
55   friend class optional;
56 
bad_optional_access(const char * reason)57   bad_optional_access(const char* reason) noexcept : reason_{reason} {}
58 
59   // String describing the reason for the bad access. Must point to a string
60   // with static storage duration.
61   const char* reason_;
62 
63   template <typename T,
64             typename std::enable_if<std::is_base_of<std::exception, T>::value, bool>::type>
65   friend constexpr void cpp17::internal::throw_or_abort(const char*);
66 };
67 
68 // A reasonably complete implementation of std::optional compatible with C++14.
69 template <typename T>
70 class optional : private ::cpp17::internal::modulate_copy_and_move<T> {
71  private:
72   // Helper types and values for SFINAE and noexcept rules.
73   static constexpr bool nothrow_move_constructible = std::is_nothrow_move_constructible<T>::value;
74 
75   static constexpr bool nothrow_swappable = std::is_nothrow_move_constructible<T>::value &&
76                                             ::cpp17::internal::is_nothrow_swappable<T>::value;
77 
78   static constexpr auto trivial_init_v = ::cpp17::internal::trivial_init_v;
79   static constexpr auto maybe_init_v = ::cpp17::internal::maybe_init_v;
80   using type_tag = ::cpp17::internal::type_tag<T>;
81 
82   template <typename U, typename V>
83   using converts_from_optional = disjunction<
84       std::is_constructible<U, const optional<V>&>, std::is_constructible<U, optional<V>&>,
85       std::is_constructible<U, const optional<V>&&>, std::is_constructible<U, optional<V>&&>,
86       std::is_convertible<const optional<V>&, U>, std::is_convertible<optional<V>&, U>,
87       std::is_convertible<const optional<V>&&, U>, std::is_convertible<optional<V>&&, U>>;
88 
89   template <typename U, typename V>
90   using assigns_from_optional =
91       disjunction<std::is_assignable<U&, const optional<V>&>, std::is_assignable<U&, optional<V>&>,
92                   std::is_assignable<U&, const optional<V>&&>,
93                   std::is_assignable<U&, optional<V>&&>>;
94 
95   template <typename U>
96   using not_self_type = ::cpp17::internal::not_same_type<optional, U>;
97 
98   template <typename U>
99   using not_in_place = ::cpp17::internal::not_same_type<in_place_t, U>;
100 
101   template <typename... Conditions>
102   using requires_conditions = ::cpp17::internal::requires_conditions<Conditions...>;
103 
104   template <typename... Conditions>
105   using assignment_requires_conditions =
106       ::cpp17::internal::assignment_requires_conditions<optional&, Conditions...>;
107 
108   template <typename... Args>
109   using emplace_constructible = std::enable_if_t<std::is_constructible<T, Args...>::value, T&>;
110 
111  public:
112   using value_type = T;
113 
114   // Default constructors.
115 
116   constexpr optional() = default;
117 
optional(nullopt_t)118   constexpr optional(nullopt_t) noexcept {}
119 
120   // Copy/move constructors and assignment operators.
121 
122   constexpr optional(const optional&) = default;
123   constexpr optional& operator=(const optional&) = default;
124 
125   constexpr optional(optional&&) = default;
126   constexpr optional& operator=(optional&&) = default;
127 
128   // Converting constructors.
129 
130   template <typename U = T,
131             requires_conditions<not_self_type<U>, not_in_place<U>, std::is_constructible<T, U&&>,
132                                 std::is_convertible<U&&, T>> = true>
optional(U && value)133   constexpr optional(U&& value) : storage_(type_tag{}, std::forward<U>(value)) {}
134 
135   template <typename U = T,
136             requires_conditions<not_self_type<U>, not_in_place<U>, std::is_constructible<T, U&&>,
137                                 negation<std::is_convertible<U&&, T>>> = false>
optional(U && value)138   explicit constexpr optional(U&& value) : storage_{type_tag{}, std::forward<U>(value)} {}
139 
140   template <typename U,
141             requires_conditions<negation<std::is_same<T, U>>, std::is_constructible<T, const U&>,
142                                 std::is_convertible<const U&, T>,
143                                 negation<converts_from_optional<T, U>>> = true>
optional(const optional<U> & other)144   constexpr optional(const optional<U>& other) : storage_{maybe_init_v, other.storage_} {}
145 
146   template <typename U,
147             requires_conditions<negation<std::is_same<T, U>>, std::is_constructible<T, const U&>,
148                                 negation<std::is_convertible<const U&, T>>,
149                                 negation<converts_from_optional<T, U>>> = false>
optional(const optional<U> & other)150   explicit constexpr optional(const optional<U>& other) : storage_{maybe_init_v, other.storage_} {}
151 
152   template <typename U,
153             requires_conditions<negation<std::is_same<T, U>>, std::is_constructible<T, U&&>,
154                                 std::is_convertible<U&&, T>,
155                                 negation<converts_from_optional<T, U>>> = true>
optional(optional<U> && other)156   constexpr optional(optional<U>&& other) : storage_{maybe_init_v, std::move(other.storage_)} {}
157 
158   template <typename U,
159             requires_conditions<negation<std::is_same<T, U>>, std::is_constructible<T, U&&>,
160                                 negation<std::is_convertible<U&&, T>>,
161                                 negation<converts_from_optional<T, U>>> = false>
optional(optional<U> && other)162   explicit constexpr optional(optional<U>&& other)
163       : storage_{maybe_init_v, std::move(other.storage_)} {}
164 
165   template <typename... Args, requires_conditions<std::is_constructible<T, Args&&...>> = false>
optional(in_place_t,Args &&...args)166   explicit constexpr optional(in_place_t, Args&&... args)
167       : storage_(type_tag{}, std::forward<Args>(args)...) {}
168 
169   template <
170       typename U, typename... Args,
171       requires_conditions<std::is_constructible<T, std::initializer_list<U>&, Args&&...>> = false>
optional(in_place_t,std::initializer_list<U> init_list,Args &&...args)172   explicit constexpr optional(in_place_t, std::initializer_list<U> init_list, Args&&... args)
173       : storage_(type_tag{}, init_list, std::forward<Args>(args)...) {}
174 
175   // Destructor.
176 
177   ~optional() = default;
178 
179   // Checked accessors.
180 
value()181   constexpr T& value() & {
182     if (has_value()) {
183       return storage_.get(type_tag{});
184     }
185     internal::throw_or_abort<bad_optional_access>("Accessed value of empty optional!");
186   }
value()187   constexpr const T& value() const& {
188     if (has_value()) {
189       return storage_.get(type_tag{});
190     }
191     internal::throw_or_abort<bad_optional_access>("Accessed value of empty optional!");
192   }
value()193   constexpr T&& value() && {
194     if (has_value()) {
195       return std::move(storage_.get(type_tag{}));
196     }
197     internal::throw_or_abort<bad_optional_access>("Accessed value of empty optional!");
198   }
value()199   constexpr const T&& value() const&& {
200     if (has_value()) {
201       return std::move(storage_.get(type_tag{}));
202     }
203     internal::throw_or_abort<bad_optional_access>("Accessed value of empty optional!");
204   }
205 
206   template <typename U>
value_or(U && default_value)207   constexpr T value_or(U&& default_value) const& {
208     static_assert(std::is_copy_constructible<T>::value,
209                   "value_or() requires copy-constructible value_type!");
210     static_assert(std::is_convertible<U&&, T>::value,
211                   "Default value must be convertible to value_type!");
212 
213     return has_value() ? storage_.get(type_tag{}) : static_cast<T>(std::forward<U>(default_value));
214   }
215   template <typename U>
value_or(U && default_value)216   constexpr T value_or(U&& default_value) && {
217     static_assert(std::is_move_constructible<T>::value,
218                   "value_or() requires move-constructible value_type!");
219     static_assert(std::is_convertible<U&&, T>::value,
220                   "Default value must be convertible to value_type!");
221 
222     return has_value() ? std::move(storage_.get(type_tag{}))
223                        : static_cast<T>(std::forward<U>(default_value));
224   }
225 
226   // Unchecked accessors.
227 
228   constexpr T* operator->() { return std::addressof(storage_.get(type_tag{})); }
229   constexpr const T* operator->() const { return std::addressof(storage_.get(type_tag{})); }
230 
231   constexpr T& operator*() { return storage_.get(type_tag{}); }
232   constexpr const T& operator*() const { return storage_.get(type_tag{}); }
233 
234   // Availability accessors/operators.
235 
has_value()236   constexpr bool has_value() const { return !storage_.is_empty(); }
237   constexpr explicit operator bool() const { return has_value(); }
238 
239   // Assignment operators.
240 
241   template <typename U>
242   constexpr assignment_requires_conditions<
243       not_self_type<U>, negation<conjunction<std::is_scalar<T>, std::is_same<T, std::decay_t<U>>>>,
244       std::is_constructible<T, U>, std::is_assignable<T&, U>>
245   operator=(U&& value) {
246     if (has_value()) {
247       storage_.get(type_tag{}) = std::forward<U>(value);
248     } else {
249       storage_.construct(type_tag{}, std::forward<U>(value));
250     }
251     return *this;
252   }
253 
254   template <typename U>
255   constexpr assignment_requires_conditions<
256       negation<std::is_same<T, U>>, std::is_constructible<T, const U&>, std::is_assignable<T&, U>,
257       negation<converts_from_optional<T, U>>, negation<assigns_from_optional<T, U>>>
258   operator=(const optional<U>& other) {
259     storage_.assign(other.storage_);
260     return *this;
261   }
262 
263   template <typename U>
264   constexpr assignment_requires_conditions<
265       negation<std::is_same<T, U>>, std::is_constructible<T, U>, std::is_assignable<T&, U>,
266       negation<converts_from_optional<T, U>>, negation<assigns_from_optional<T, U>>>
267   operator=(optional<U>&& other) {
268     storage_.assign(std::move(other.storage_));
269     return *this;
270   }
271 
272   constexpr optional& operator=(nullopt_t) {
273     storage_.reset();
274     return *this;
275   }
276 
277   // Swap.
278 
swap(optional & other)279   constexpr void swap(optional& other) noexcept(nothrow_swappable) {
280     storage_.swap(other.storage_);
281   }
282 
283   // Emplacement.
284 
285   template <typename... Args>
emplace(Args &&...args)286   constexpr emplace_constructible<Args&&...> emplace(Args&&... args) {
287     storage_.reset();
288     storage_.construct(type_tag{}, std::forward<Args>(args)...);
289     return storage_.get(type_tag{});
290   }
291 
292   template <typename U, typename... Args>
emplace(std::initializer_list<U> init_list,Args &&...args)293   constexpr emplace_constructible<std::initializer_list<U>&, Args&&...> emplace(
294       std::initializer_list<U> init_list, Args&&... args) {
295     storage_.reset();
296     storage_.construct(type_tag{}, init_list, std::forward<Args>(args)...);
297     return storage_.get(type_tag{});
298   }
299 
300   // Reset.
301 
reset()302   void reset() noexcept { storage_.reset(); }
303 
304  private:
305   ::cpp17::internal::storage_type<T> storage_;
306 };
307 
308 // Swap.
309 template <typename T>
310 inline std::enable_if_t<(std::is_move_constructible<T>::value &&
311                          ::cpp17::internal::is_swappable<T>::value)>
swap(optional<T> & a,optional<T> & b)312 swap(optional<T>& a, optional<T>& b) noexcept(noexcept(a.swap(b))) {
313   a.swap(b);
314 }
315 template <typename T>
316 inline std::enable_if_t<(!std::is_move_constructible<T>::value &&
317                          ::cpp17::internal::is_swappable<T>::value)>
318 swap(optional<T>& a, optional<T>& b) = delete;
319 
320 // Make optional.
321 template <typename T>
make_optional(T && value)322 constexpr optional<std::decay_t<T>> make_optional(T&& value) {
323   return optional<std::decay_t<T>>{std::forward<T>(value)};
324 }
325 template <typename T, typename... Args>
make_optional(Args &&...args)326 constexpr optional<T> make_optional(Args&&... args) {
327   return optional<T>{in_place, std::forward<Args>(args)...};
328 }
329 template <typename T, typename U, typename... Args>
make_optional(std::initializer_list<U> init_list,Args &&...args)330 constexpr optional<T> make_optional(std::initializer_list<U> init_list, Args&&... args) {
331   return optional<T>{in_place, init_list, std::forward<Args>(args)...};
332 }
333 
334 // Empty.
335 template <typename T>
336 constexpr bool operator==(const optional<T>& lhs, nullopt_t) {
337   return !lhs.has_value();
338 }
339 template <typename T>
340 constexpr bool operator!=(const optional<T>& lhs, nullopt_t) {
341   return lhs.has_value();
342 }
343 
344 template <typename T>
345 constexpr bool operator==(nullopt_t, const optional<T>& rhs) {
346   return !rhs.has_value();
347 }
348 template <typename T>
349 constexpr bool operator!=(nullopt_t, const optional<T>& rhs) {
350   return rhs.has_value();
351 }
352 
353 // Equal/not equal.
354 template <
355     typename T, typename U,
356     ::cpp17::internal::enable_relop_t<decltype(std::declval<T>() == std::declval<U>())> = true>
357 constexpr bool operator==(const optional<T>& lhs, const optional<U>& rhs) {
358   return (lhs.has_value() == rhs.has_value()) && (!lhs.has_value() || *lhs == *rhs);
359 }
360 template <
361     typename T, typename U,
362     ::cpp17::internal::enable_relop_t<decltype(std::declval<T>() != std::declval<U>())> = true>
363 constexpr bool operator!=(const optional<T>& lhs, const optional<U>& rhs) {
364   return (lhs.has_value() != rhs.has_value()) || (lhs.has_value() && *lhs != *rhs);
365 }
366 
367 template <typename T, typename U,
368           ::cpp17::internal::enable_relop_t<decltype(std::declval<T>() == std::declval<U>()),
369                                             ::cpp17::internal::not_same_type<nullopt_t, U>> = true>
370 constexpr bool operator==(const optional<T>& lhs, const U& rhs) {
371   return lhs.has_value() && *lhs == rhs;
372 }
373 template <typename T, typename U,
374           ::cpp17::internal::enable_relop_t<decltype(std::declval<T>() != std::declval<U>()),
375                                             ::cpp17::internal::not_same_type<nullopt_t, U>> = true>
376 constexpr bool operator!=(const optional<T>& lhs, const U& rhs) {
377   return !lhs.has_value() || *lhs != rhs;
378 }
379 
380 template <typename T, typename U,
381           ::cpp17::internal::enable_relop_t<decltype(std::declval<T>() == std::declval<U>()),
382                                             ::cpp17::internal::not_same_type<nullopt_t, T>> = true>
383 constexpr bool operator==(const T& lhs, const optional<U>& rhs) {
384   return rhs.has_value() && lhs == *rhs;
385 }
386 template <typename T, typename U,
387           ::cpp17::internal::enable_relop_t<decltype(std::declval<T>() != std::declval<U>()),
388                                             ::cpp17::internal::not_same_type<nullopt_t, T>> = true>
389 constexpr bool operator!=(const T& lhs, const optional<U>& rhs) {
390   return !rhs.has_value() || lhs != *rhs;
391 }
392 
393 // Less than/greater than.
394 template <typename T, typename U,
395           ::cpp17::internal::enable_relop_t<decltype(std::declval<T>() < std::declval<U>())> = true>
396 constexpr bool operator<(const optional<T>& lhs, const optional<U>& rhs) {
397   return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs);
398 }
399 template <typename T, typename U,
400           ::cpp17::internal::enable_relop_t<decltype(std::declval<T>() > std::declval<U>())> = true>
401 constexpr bool operator>(const optional<T>& lhs, const optional<U>& rhs) {
402   return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs);
403 }
404 
405 template <typename T, typename U,
406           ::cpp17::internal::enable_relop_t<decltype(std::declval<T>() < std::declval<U>()),
407                                             ::cpp17::internal::not_same_type<nullopt_t, U>> = true>
408 constexpr bool operator<(const optional<T>& lhs, const U& rhs) {
409   return !lhs.has_value() || *lhs < rhs;
410 }
411 template <typename T, typename U,
412           ::cpp17::internal::enable_relop_t<decltype(std::declval<T>() > std::declval<U>()),
413                                             ::cpp17::internal::not_same_type<nullopt_t, U>> = true>
414 constexpr bool operator>(const optional<T>& lhs, const U& rhs) {
415   return lhs.has_value() && *lhs > rhs;
416 }
417 
418 template <typename T, typename U,
419           ::cpp17::internal::enable_relop_t<decltype(std::declval<T>() < std::declval<U>()),
420                                             ::cpp17::internal::not_same_type<nullopt_t, T>> = true>
421 constexpr bool operator<(const T& lhs, const optional<U>& rhs) {
422   return rhs.has_value() && lhs < *rhs;
423 }
424 template <typename T, typename U,
425           ::cpp17::internal::enable_relop_t<decltype(std::declval<T>() > std::declval<U>()),
426                                             ::cpp17::internal::not_same_type<nullopt_t, T>> = true>
427 constexpr bool operator>(const T& lhs, const optional<U>& rhs) {
428   return !rhs.has_value() || lhs > *rhs;
429 }
430 
431 // Less than or equal/greater than or equal.
432 template <
433     typename T, typename U,
434     ::cpp17::internal::enable_relop_t<decltype(std::declval<T>() <= std::declval<U>())> = true>
435 constexpr bool operator<=(const optional<T>& lhs, const optional<U>& rhs) {
436   return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs);
437 }
438 template <
439     typename T, typename U,
440     ::cpp17::internal::enable_relop_t<decltype(std::declval<T>() >= std::declval<U>())> = true>
441 constexpr bool operator>=(const optional<T>& lhs, const optional<U>& rhs) {
442   return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs);
443 }
444 
445 template <typename T, typename U,
446           ::cpp17::internal::enable_relop_t<decltype(std::declval<T>() <= std::declval<U>()),
447                                             ::cpp17::internal::not_same_type<nullopt_t, U>> = true>
448 constexpr bool operator<=(const optional<T>& lhs, const U& rhs) {
449   return !lhs.has_value() || *lhs <= rhs;
450 }
451 template <typename T, typename U,
452           ::cpp17::internal::enable_relop_t<decltype(std::declval<T>() >= std::declval<U>()),
453                                             ::cpp17::internal::not_same_type<nullopt_t, U>> = true>
454 constexpr bool operator>=(const optional<T>& lhs, const U& rhs) {
455   return lhs.has_value() && *lhs >= rhs;
456 }
457 
458 template <typename T, typename U,
459           ::cpp17::internal::enable_relop_t<decltype(std::declval<T>() <= std::declval<U>()),
460                                             ::cpp17::internal::not_same_type<nullopt_t, T>> = true>
461 constexpr bool operator<=(const T& lhs, const optional<U>& rhs) {
462   return rhs.has_value() && lhs <= *rhs;
463 }
464 template <typename T, typename U,
465           ::cpp17::internal::enable_relop_t<decltype(std::declval<T>() >= std::declval<U>()),
466                                             ::cpp17::internal::not_same_type<nullopt_t, T>> = true>
467 constexpr bool operator>=(const T& lhs, const optional<U>& rhs) {
468   return !rhs.has_value() || lhs >= *rhs;
469 }
470 
471 }  // namespace cpp17
472 
473 #endif
474 
475 #endif  // LIB_STDCOMPAT_INCLUDE_LIB_STDCOMPAT_OPTIONAL_H_
476