1 /*************************************************************************
3 * Copyright 2016 Realm Inc.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 **************************************************************************/
20 #ifndef REALM_UTIL_OPTIONAL_HPP
21 #define REALM_UTIL_OPTIONAL_HPP
23 #include <realm/util/features.h>
25 #include <stdexcept> // std::logic_error
26 #include <functional> // std::less
34 // some() should be the equivalent of the proposed C++17 `make_optional`.
35 template <class T, class... Args>
36 Optional<T> some(Args&&...);
40 // Note: Should conform with the future std::nullopt_t and std::in_place_t.
42 constexpr explicit None(int)
46 static constexpr None none{0};
52 static constexpr InPlace in_place;
54 // Note: Should conform with the future std::bad_optional_access.
55 struct BadOptionalAccess : std::logic_error {
56 explicit BadOptionalAccess(const std::string& what_arg)
57 : std::logic_error(what_arg)
60 explicit BadOptionalAccess(const char* what_arg)
61 : std::logic_error(what_arg)
70 template <class T, bool = std::is_trivially_destructible<T>::value>
71 struct OptionalStorage;
73 template <class T, class U>
74 struct TypeIsAssignableToOptional {
75 // Constraints from [optional.object.assign.18]
76 static const bool value = (std::is_same<typename std::remove_reference<U>::type, T>::value &&
77 std::is_constructible<T, U>::value && std::is_assignable<T&, U>::value);
84 // Note: Should conform with the future std::optional.
86 class Optional : private _impl::OptionalStorage<T> {
91 constexpr Optional(None);
92 Optional(Optional<T>&& other);
93 Optional(const Optional<T>& other);
95 constexpr Optional(T&& value);
96 constexpr Optional(const T& value);
98 template <class... Args>
99 constexpr Optional(InPlace tag, Args&&...);
100 // FIXME: std::optional specifies an std::initializer_list constructor overload as well.
102 Optional<T>& operator=(None);
103 Optional<T>& operator=(Optional<T>&& other);
104 Optional<T>& operator=(const Optional<T>& other);
106 template <class U, class = typename std::enable_if<_impl::TypeIsAssignableToOptional<T, U>::value>::type>
107 Optional<T>& operator=(U&& value);
109 explicit constexpr operator bool() const;
110 constexpr const T& value() const; // Throws
111 T& value(); // Throws, FIXME: Can be constexpr with C++14
112 constexpr const T& operator*() const; // Throws
113 T& operator*(); // Throws, FIXME: Can be constexpr with C++14
114 constexpr const T* operator->() const; // Throws
115 T* operator->(); // Throws, FIXME: Can be constexpr with C++14
118 constexpr T value_or(U&& value) const &;
121 T value_or(U&& value) &&;
123 void swap(Optional<T>& other); // FIXME: Add noexcept() clause
125 template <class... Args>
126 void emplace(Args&&...);
127 // FIXME: std::optional specifies an std::initializer_list overload for `emplace` as well.
129 using Storage = _impl::OptionalStorage<T>;
130 using Storage::m_engaged;
131 using Storage::m_value;
133 constexpr bool is_engaged() const
137 void set_engaged(bool b)
144 /// An Optional<void> is functionally equivalent to a bool.
145 /// Note: C++17 does not (yet) specify this specialization, but it is convenient
146 /// as a "safer bool", especially in the presence of `fmap`.
147 /// Disabled for compliance with std::optional.
149 // class Optional<void> {
153 // Optional(Optional<void>&&) = default;
154 // Optional(const Optional<void>&) = default;
155 // explicit operator bool() const { return m_engaged; }
157 // bool m_engaged = false;
158 // friend struct Some<void>;
161 /// An Optional<T&> is a non-owning nullable pointer that throws on dereference.
162 // FIXME: Visual Studio 2015's constexpr support isn't sufficient to allow Optional<T&> to compile
163 // in constexpr contexts.
167 using value_type = T&;
168 using target_type = typename std::decay<T>::type;
173 constexpr Optional(None)
175 } // FIXME: Was a delegating constructor, but not fully supported in VS2015
176 Optional(const Optional<T&>& other) = default;
178 Optional(const Optional<U&>& other)
183 Optional(std::reference_wrapper<U> ref)
188 constexpr Optional(T& init_value)
192 Optional(T&& value) = delete; // Catches accidental references to rvalue temporaries.
194 Optional<T&>& operator=(None)
199 Optional<T&>& operator=(const Optional<T&>& other)
206 Optional<T&>& operator=(std::reference_wrapper<U> ref)
212 explicit constexpr operator bool() const
216 constexpr const target_type& value() const; // Throws
217 target_type& value(); // Throws
218 constexpr const target_type& operator*() const
222 target_type& operator*()
226 constexpr const target_type* operator->() const
230 target_type* operator->()
235 void swap(Optional<T&> other); // FIXME: Add noexcept() clause
240 friend class Optional;
245 struct RemoveOptional {
249 struct RemoveOptional<Optional<T>> {
250 using type = typename RemoveOptional<T>::type; // Remove recursively
258 template <class... Args>
259 static Optional<T> some(Args&&... args)
261 return Optional<T>{std::forward<Args>(args)...};
265 /// Disabled for compliance with std::optional.
267 // struct Some<void> {
268 // static Optional<void> some()
270 // Optional<void> opt;
271 // opt.m_engaged = true;
276 template <class T, class... Args>
277 Optional<T> some(Args&&... args)
279 return Some<T>::some(std::forward<Args>(args)...);
284 constexpr Optional<T>::Optional()
290 constexpr Optional<T>::Optional(None)
296 Optional<T>::Optional(Optional<T>&& other)
299 if (other.m_engaged) {
300 new (&m_value) T(std::move(other.m_value));
306 Optional<T>::Optional(const Optional<T>& other)
309 if (other.m_engaged) {
310 new (&m_value) T(other.m_value);
316 constexpr Optional<T>::Optional(T&& r_value)
317 : Storage(std::move(r_value))
322 constexpr Optional<T>::Optional(const T& l_value)
328 template <class... Args>
329 constexpr Optional<T>::Optional(InPlace, Args&&... args)
330 : Storage(std::forward<Args>(args)...)
335 void Optional<T>::clear()
344 Optional<T>& Optional<T>::operator=(None)
351 Optional<T>& Optional<T>::operator=(Optional<T>&& other)
354 if (other.m_engaged) {
355 m_value = std::move(other.m_value);
362 if (other.m_engaged) {
363 new (&m_value) T(std::move(other.m_value));
371 Optional<T>& Optional<T>::operator=(const Optional<T>& other)
374 if (other.m_engaged) {
375 m_value = other.m_value;
382 if (other.m_engaged) {
383 new (&m_value) T(other.m_value);
391 template <class U, class>
392 Optional<T>& Optional<T>::operator=(U&& r_value)
395 m_value = std::forward<U>(r_value);
398 new (&m_value) T(std::forward<U>(r_value));
405 constexpr Optional<T>::operator bool() const
411 constexpr const T& Optional<T>::value() const
413 return m_engaged ? m_value : (throw BadOptionalAccess{"bad optional access"}, m_value);
417 T& Optional<T>::value()
420 throw BadOptionalAccess{"bad optional access"};
426 constexpr const typename Optional<T&>::target_type& Optional<T&>::value() const
428 return m_ptr ? *m_ptr : (throw BadOptionalAccess{"bad optional access"}, *m_ptr);
432 typename Optional<T&>::target_type& Optional<T&>::value()
435 throw BadOptionalAccess{"bad optional access"};
441 constexpr const T& Optional<T>::operator*() const
443 // Note: This differs from std::optional, which doesn't throw.
448 T& Optional<T>::operator*()
450 // Note: This differs from std::optional, which doesn't throw.
455 constexpr const T* Optional<T>::operator->() const
457 // Note: This differs from std::optional, which doesn't throw.
462 T* Optional<T>::operator->()
464 // Note: This differs from std::optional, which doesn't throw.
470 constexpr T Optional<T>::value_or(U&& otherwise) const &
472 return m_engaged ? T{m_value} : T{std::forward<U>(otherwise)};
477 T Optional<T>::value_or(U&& otherwise) &&
480 return T(std::move(m_value));
483 return T(std::forward<U>(otherwise));
488 void Optional<T>::swap(Optional<T>& other)
490 // FIXME: This might be optimizable.
491 Optional<T> tmp = std::move(other);
492 other = std::move(*this);
493 *this = std::move(tmp);
497 template <class... Args>
498 void Optional<T>::emplace(Args&&... args)
501 new (&m_value) T(std::forward<Args>(args)...);
507 constexpr Optional<typename std::decay<T>::type> make_optional(T&& value)
509 using Type = typename std::decay<T>::type;
510 return some<Type>(std::forward<T>(value));
514 bool operator==(const Optional<T>& lhs, const Optional<T>& rhs)
526 bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs)
528 return !(lhs == rhs);
532 bool operator<(const Optional<T>& lhs, const Optional<T>& rhs)
540 return std::less<T>{}(*lhs, *rhs);
544 bool operator>(const util::Optional<T>& lhs, const util::Optional<T>& rhs)
552 return std::greater<T>{}(*lhs, *rhs);
556 bool operator==(const Optional<T>& lhs, None)
562 bool operator!=(const Optional<T>& lhs, None)
568 bool operator<(const Optional<T>& lhs, None)
570 static_cast<void>(lhs);
575 bool operator==(None, const Optional<T>& rhs)
581 bool operator!=(None, const Optional<T>& rhs)
587 bool operator<(None, const Optional<T>& rhs)
592 template <class T, class U>
593 bool operator==(const Optional<T>& lhs, const U& rhs)
595 return lhs ? *lhs == rhs : false;
599 bool operator<(const Optional<T>& lhs, const T& rhs)
601 return lhs ? std::less<T>{}(*lhs, rhs) : true;
604 template <class T, class U>
605 bool operator==(const T& lhs, const Optional<U>& rhs)
607 return rhs ? lhs == *rhs : false;
611 bool operator<(const T& lhs, const Optional<T>& rhs)
613 return rhs ? std::less<T>{}(lhs, *rhs) : false;
616 template <class T, class F>
617 auto operator>>(Optional<T> lhs, F&& rhs) -> decltype(fmap(lhs, std::forward<F>(rhs)))
619 return fmap(lhs, std::forward<F>(rhs));
622 template <class OS, class T>
623 OS& operator<<(OS& os, const Optional<T>& rhs)
626 os << "some(" << *rhs << ")";
641 T unwrap(util::Optional<T>&& value)
647 T unwrap(const util::Optional<T>& value)
653 T unwrap(util::Optional<T>& value)
662 // T is trivially destructible.
664 struct OptionalStorage<T, true> {
669 bool m_engaged = false;
671 constexpr OptionalStorage(realm::util::None)
675 constexpr OptionalStorage(T&& value)
676 : m_value(std::move(value))
681 template <class... Args>
682 constexpr OptionalStorage(Args&&... args)
689 // T is not trivially destructible.
691 struct OptionalStorage<T, false> {
696 bool m_engaged = false;
698 constexpr OptionalStorage(realm::util::None)
702 constexpr OptionalStorage(T&& value)
703 : m_value(std::move(value))
708 template <class... Args>
709 constexpr OptionalStorage(Args&&... args)
728 #endif // REALM_UTIL_OPTIONAL_HPP