/* * Copyright 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include namespace android::ftl { // Superset of std::optional with monadic operations, as proposed in https://wg21.link/P0798R8. // // TODO: Remove standard APIs in C++23. // template struct Optional final : std::optional { using std::optional::optional; // Implicit downcast. Optional(std::optional other) : std::optional(std::move(other)) {} using std::optional::has_value; using std::optional::value; // Returns Optional where F is a function that maps T to U. template constexpr auto transform(F&& f) const& { using R = details::transform_result_t; if (has_value()) return R(std::invoke(std::forward(f), value())); return R(); } template constexpr auto transform(F&& f) & { using R = details::transform_result_t; if (has_value()) return R(std::invoke(std::forward(f), value())); return R(); } template constexpr auto transform(F&& f) const&& { using R = details::transform_result_t; if (has_value()) return R(std::invoke(std::forward(f), std::move(value()))); return R(); } template constexpr auto transform(F&& f) && { using R = details::transform_result_t; if (has_value()) return R(std::invoke(std::forward(f), std::move(value()))); return R(); } // Returns Optional where F is a function that maps T to Optional. template constexpr auto and_then(F&& f) const& { using R = details::and_then_result_t; if (has_value()) return std::invoke(std::forward(f), value()); return R(); } template constexpr auto and_then(F&& f) & { using R = details::and_then_result_t; if (has_value()) return std::invoke(std::forward(f), value()); return R(); } template constexpr auto and_then(F&& f) const&& { using R = details::and_then_result_t; if (has_value()) return std::invoke(std::forward(f), std::move(value())); return R(); } template constexpr auto and_then(F&& f) && { using R = details::and_then_result_t; if (has_value()) return std::invoke(std::forward(f), std::move(value())); return R(); } // Returns this Optional if not nullopt, or else the Optional returned by the function F. template constexpr auto or_else(F&& f) const& -> details::or_else_result_t { if (has_value()) return *this; return std::forward(f)(); } template constexpr auto or_else(F&& f) && -> details::or_else_result_t { if (has_value()) return std::move(*this); return std::forward(f)(); } // Maps this Optional to expected where nullopt becomes E. template constexpr auto ok_or(E&& e) && -> base::expected { if (has_value()) return std::move(value()); return base::unexpected(std::forward(e)); } // Delete new for this class. Its base doesn't have a virtual destructor, and // if it got deleted via base class pointer, it would cause undefined // behavior. There's not a good reason to allocate this object on the heap // anyway. static void* operator new(size_t) = delete; static void* operator new[](size_t) = delete; }; template constexpr bool operator==(const Optional& lhs, const Optional& rhs) { return static_cast>(lhs) == static_cast>(rhs); } template constexpr bool operator!=(const Optional& lhs, const Optional& rhs) { return !(lhs == rhs); } // Deduction guides. template Optional(T) -> Optional; template Optional(std::optional) -> Optional; } // namespace android::ftl