// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_TYPES_EXPECTED_H_ #define BASE_TYPES_EXPECTED_H_ #include <concepts> #include <string_view> #include <type_traits> #include <utility> #include "base/check.h" #include "base/strings/strcat.h" #include "base/strings/to_string.h" #include "base/types/expected_internal.h" // IWYU pragma: export #include "third_party/abseil-cpp/absl/utility/utility.h" // Class template `expected<T, E>` is a vocabulary type which contains an // expected value of type `T`, or an error `E`. The class skews towards behaving // like a `T`, because its intended use is when the expected type is contained. // When something unexpected occurs, more typing is required. When all is good, // code mostly looks as if a `T` were being handled. // // Class template `expected<T, E>` contains either: // * A value of type `T`, the expected value type; or // * A value of type `E`, an error type used when an unexpected outcome // occurred. // // The interface can be queried as to whether the underlying value is the // expected value (of type `T`) or an unexpected value (of type `E`). The // interface and the rational are based on `std::optional`. We consider // `expected<T, E>` as a supplement to `optional<T>`, expressing why an expected // value isn’t contained in the object. // // Example Usage: // // Before: // bool ParseInt32(std::string_view input, // int32_t* output, // ParseIntError* error); // ... // // int32_t output; // ParseIntError error; // if (ParseInt32("...". &output, &error)) { // // process `output` // } else { // // process `error` // } // // After: // // base::expected<int32_t, ParseIntError> ParseInt32(std::string_view input); // ... // // if (auto parsed = ParseInt32("..."); parsed.has_value()) { // // process `parsed.value()` // } else { // // process `parsed.error()` // } // // For even less boilerplate, see expected_macros.h. // // Note that there are various transformation member functions. To avoid having // to puzzle through the standard-ese on their documentation, here's a quick // reference table, assuming a source `expected<T, E> ex;` and types U and G // convertible from T and E, respectively: // // Return type Val when ex = t Val when ex = e // ----------- --------------- --------------- // ex.value_or(t2) T t t2 // ex.and_then(f) expected<U, E> f(t) unexpected(e) // ex.transform(f) expected<U, E> expected(f(t)) unexpected(e) // ex.or_else(f) expected<T, G> expected(t) f(e) // ex.transform_error(f) expected<T, G> expected(t) unexpected(f(e)) // // References: // * https://wg21.link/P0323 // * https://eel.is/c++draft/expected namespace base { // Note: base::unexpected and base::expected are C++17 compatible backports of // C++23's std::unexpected and std::expected. They differ from the standard in // the following ways: // // * Not all member functions can be used in a constexpr context. This is due to // limitations in both the C++17 language and the Abseil library used for the // implementation. // * Since Chromium does not support exceptions, there is no bad_expected_access // exception and the program will just terminate when the exception would have // been thrown. Furthermore, all member functions are marked noexcept. // * C++23 allows an implicit conversion from U to expected<T, E> if U is // implicitly convertible to T; the Chromium version only allows an implicit // conversion if U is implicitly convertible to T *and* U is *not* implicitly // convertible to E, to guard against bug-prone patterns such as: // // creates an expected value containing true, not an unexpected value // // containing 123L. // expected<bool, long> e = 123L; // * Because of the above restriction, the Chromium version also introduces // `base::ok` as a complement to `base::unexpected` to simplify returning // success values when the implicit conversion above is disallowed. // * Calling operator* or operator-> on an unexpected value results in program // termination, and not UB. // * There is no operator bool due to bug-prone usage when the value type is // convertible to bool, see e.g. https://abseil.io/tips/141. // * Moving out of an expected object will put it into a moved-from state. // Trying to use it before re-initializing it will result in program // termination. // * The expected<void> specialization is done via a defaulted boolean template // parameter, due to the lack of requires clauses in C++17. // * Since equality operators can not be defaulted in C++17, equality and // inequality operators are specified explicitly. // * base::expected implements the monadic interface proposal // (https://wg21.link/P2505), which is currently only on track for C++26. // Class template used as a type hint for constructing a `base::expected` // containing a value (i.e. success). Useful when implicit conversion // construction of `base::expected` is disallowed, e.g. due to ambiguity. // Example usage: // // base::expected<std::string, std::string> RunOp() { // std::string error; // std::string result = RunOpImpl(..., &error); // if (!error.empty()) { // return base::unexpected(std::move(error)); // // // The C++23 std::expected proposal allows this to be simply written as // // return result; // // // // However, the Chromium version disallows this if E implicitly converts // // to T, so without base::ok(), this would have to be written as: // // return base::expected<std::string, std::string>(std::move(result)); // // return base::ok(std::move(result)); // } template <typename T> class ok final { … }; ok<T>; template <typename T, typename U> constexpr bool operator==(const ok<T>& lhs, const ok<U>& rhs) noexcept { … } template <typename T, typename U> constexpr bool operator!=(const ok<T>& lhs, const ok<U>& rhs) noexcept { … } template <typename T> ok(T) -> ok<T>; <deduction guide for ok>(); // [expected.un.object], class template unexpected // https://eel.is/c++draft/expected#un.object template <typename E> class unexpected final { … }; // [expected.un.eq] Equality operator template <typename E, typename G> constexpr bool operator==(const unexpected<E>& lhs, const unexpected<G>& rhs) noexcept { … } template <typename E, typename G> constexpr bool operator!=(const unexpected<E>& lhs, const unexpected<G>& rhs) noexcept { … } template <typename E> unexpected(E) -> unexpected<E>; // [expected.expected], class template expected // https://eel.is/c++draft/expected#expected template <typename T, typename E> class [[nodiscard]] expected final { … }; // [expected.void], partial specialization of expected for void types expected<T, E>; // [expected.object.eq], equality operators // [expected.void.eq], equality operators template <typename T, typename E, typename U, typename G> constexpr bool operator==(const expected<T, E>& x, const expected<U, G>& y) noexcept { … } template <typename T, typename E, typename U> requires(!std::is_void_v<T>) constexpr bool operator==(const expected<T, E>& x, const U& v) noexcept { … } template <typename T, typename E, typename U> constexpr bool operator==(const expected<T, E>& x, const ok<U>& o) noexcept { … } template <typename T, typename E, typename G> constexpr bool operator==(const expected<T, E>& x, const unexpected<G>& e) noexcept { … } } // namespace base #endif // BASE_TYPES_EXPECTED_H_