1 /* 2 * Copyright 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #pragma once 18 19 #include <functional> 20 #include <optional> 21 #include <utility> 22 23 #include <ftl/details/optional.h> 24 25 namespace android::ftl { 26 27 // Superset of std::optional<T> with monadic operations, as proposed in https://wg21.link/P0798R8. 28 // 29 // TODO: Remove in C++23. 30 // 31 template <typename T> 32 struct Optional final : std::optional<T> { 33 using std::optional<T>::optional; 34 35 // Implicit downcast. Optionalfinal36 Optional(std::optional<T> other) : std::optional<T>(std::move(other)) {} 37 38 using std::optional<T>::has_value; 39 using std::optional<T>::value; 40 41 // Returns Optional<U> where F is a function that maps T to U. 42 template <typename F> transformfinal43 constexpr auto transform(F&& f) const& { 44 using R = details::transform_result_t<F, decltype(value())>; 45 if (has_value()) return R(std::invoke(std::forward<F>(f), value())); 46 return R(); 47 } 48 49 template <typename F> transformfinal50 constexpr auto transform(F&& f) & { 51 using R = details::transform_result_t<F, decltype(value())>; 52 if (has_value()) return R(std::invoke(std::forward<F>(f), value())); 53 return R(); 54 } 55 56 template <typename F> transformfinal57 constexpr auto transform(F&& f) const&& { 58 using R = details::transform_result_t<F, decltype(std::move(value()))>; 59 if (has_value()) return R(std::invoke(std::forward<F>(f), std::move(value()))); 60 return R(); 61 } 62 63 template <typename F> transformfinal64 constexpr auto transform(F&& f) && { 65 using R = details::transform_result_t<F, decltype(std::move(value()))>; 66 if (has_value()) return R(std::invoke(std::forward<F>(f), std::move(value()))); 67 return R(); 68 } 69 70 // Returns Optional<U> where F is a function that maps T to Optional<U>. 71 template <typename F> and_thenfinal72 constexpr auto and_then(F&& f) const& { 73 using R = details::and_then_result_t<F, decltype(value())>; 74 if (has_value()) return std::invoke(std::forward<F>(f), value()); 75 return R(); 76 } 77 78 template <typename F> and_thenfinal79 constexpr auto and_then(F&& f) & { 80 using R = details::and_then_result_t<F, decltype(value())>; 81 if (has_value()) return std::invoke(std::forward<F>(f), value()); 82 return R(); 83 } 84 85 template <typename F> and_thenfinal86 constexpr auto and_then(F&& f) const&& { 87 using R = details::and_then_result_t<F, decltype(std::move(value()))>; 88 if (has_value()) return std::invoke(std::forward<F>(f), std::move(value())); 89 return R(); 90 } 91 92 template <typename F> and_thenfinal93 constexpr auto and_then(F&& f) && { 94 using R = details::and_then_result_t<F, decltype(std::move(value()))>; 95 if (has_value()) return std::invoke(std::forward<F>(f), std::move(value())); 96 return R(); 97 } 98 99 // Returns this Optional<T> if not nullopt, or else the Optional<T> returned by the function F. 100 template <typename F> 101 constexpr auto or_else(F&& f) const& -> details::or_else_result_t<F, T> { 102 if (has_value()) return *this; 103 return std::forward<F>(f)(); 104 } 105 106 template <typename F> 107 constexpr auto or_else(F&& f) && -> details::or_else_result_t<F, T> { 108 if (has_value()) return std::move(*this); 109 return std::forward<F>(f)(); 110 } 111 112 // Delete new for this class. Its base doesn't have a virtual destructor, and 113 // if it got deleted via base class pointer, it would cause undefined 114 // behavior. There's not a good reason to allocate this object on the heap 115 // anyway. 116 static void* operator new(size_t) = delete; 117 static void* operator new[](size_t) = delete; 118 }; 119 120 template <typename T, typename U> 121 constexpr bool operator==(const Optional<T>& lhs, const Optional<U>& rhs) { 122 return static_cast<std::optional<T>>(lhs) == static_cast<std::optional<U>>(rhs); 123 } 124 125 template <typename T, typename U> 126 constexpr bool operator!=(const Optional<T>& lhs, const Optional<U>& rhs) { 127 return !(lhs == rhs); 128 } 129 130 // Deduction guides. 131 template <typename T> 132 Optional(T) -> Optional<T>; 133 134 template <typename T> 135 Optional(std::optional<T>) -> Optional<T>; 136 137 } // namespace android::ftl 138