// Copyright 2023 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_MACROS_H_ #define BASE_TYPES_EXPECTED_MACROS_H_ #include <functional> #include <string_view> #include <type_traits> #include <utility> #include "base/compiler_specific.h" #include "base/macros/concat.h" #include "base/macros/remove_parens.h" #include "base/macros/uniquify.h" #include "base/memory/raw_ptr_exclusion.h" #include "base/types/expected.h" // Executes an expression `rexpr` that returns a `base::expected<T, E>`. If the // result is an error, causes the calling function to return. If no additional // arguments are given, the function return value is the `E` returned by // `rexpr`. Otherwise, the additional arguments are treated as an invocable that // expects an E as its last argument and returns some type (including `void`) // convertible to the function's return type; that is, the function returns the // result of `std::invoke(..., E)`. // // This works with move-only types and can be used in functions that return // either an `E` directly or a `base::expected<U, E>`, without needing to // explicitly wrap the return in `base::unexpected`. // // # Interface // // `RETURN_IF_ERROR(rexpr, ...);` // // # Examples // // Use with no additional arguments: // ``` // SomeErrorCode Foo() { // RETURN_IF_ERROR(Function(args...)); // return SomeErrorCode::kOK; // } // ``` // ``` // base::expected<int, SomeErrorCode> Bar() { // RETURN_IF_ERROR(Function(args...)); // RETURN_IF_ERROR(obj.Method(args...)); // return 17; // } // ``` // // Adjusting the returned error: // ``` // RETURN_IF_ERROR(TryProcessing(query), // [](const auto& e) { // return base::StrCat({e, " while processing query"}); // }); // ``` // // Returning a different kind of error: // ``` // RETURN_IF_ERROR(TryProcessing(query), // [](auto) { return SomeErrorCode::kFail); }); // ``` // // Returning void: // ``` // RETURN_IF_ERROR(TryProcessing(query), [](auto) {}); // ``` // ``` // RETURN_IF_ERROR(TryProcessing(query), // [](auto) { LOG(WARNING) << "Uh oh"; }()); // ``` // // Automatic conversion to `base::expected<U, E>`: // ``` // base::expected<int, SomeErrorCode> Foo() { // RETURN_IF_ERROR(TryProcessing(query), // [](auto) { return SomeErrorCode::kFail); }); // return 17; // } // ``` // // Passing the error to a static/global handler: // ``` // RETURN_IF_ERROR(TryProcessing(query), &FailureHandler); // ``` // // Passing the error to a handler member function: // ``` // RETURN_IF_ERROR(TryProcessing(query), &MyClass::FailureHandler, this); // ``` #define RETURN_IF_ERROR(rexpr, ...) … // Executes an expression `rexpr` that returns a `base::expected<T, E>`. If the // result is an expected value, moves the `T` into whatever `lhs` defines/refers // to; otherwise, behaves like RETURN_IF_ERROR() above. Avoid side effects in // `lhs`, as it will not be evaluated in the error case. // // # Interface // // `ASSIGN_OR_RETURN(lhs, rexpr, ...);` // // WARNING: If `lhs` is parenthesized, the parentheses are removed; for this // reason, `lhs` may not contain a ternary (`?:`). See examples for // motivation. // // WARNING: Expands into multiple statements; cannot be used in a single // statement (e.g. as the body of an `if` statement without `{}`)! // // # Examples // // Declaring and initializing a new variable (ValueType can be anything that can // be initialized with assignment): // ``` // ASSIGN_OR_RETURN(ValueType value, MaybeGetValue(arg)); // ``` // // Assigning to an existing variable: // ``` // ValueType value; // ASSIGN_OR_RETURN(value, MaybeGetValue(arg)); // ``` // // Initializing a `std::unique_ptr`: // ``` // ASSIGN_OR_RETURN(std::unique_ptr<T> ptr, MaybeGetPtr(arg)); // ``` // // Initializing a map. Because of C++ preprocessor limitations, the type used in // `ASSIGN_OR_RETURN` cannot contain commas, so wrap `lhs` in parentheses: // ``` // ASSIGN_OR_RETURN((flat_map<Foo, Bar> my_map), GetMap()); // ``` // Or use `auto` if the type is obvious enough: // ``` // ASSIGN_OR_RETURN(auto code_widget, GetCodeWidget()); // ``` // // Assigning to structured bindings. The same situation with comma as above, so // wrap `lhs` in parentheses: // ``` // ASSIGN_OR_RETURN((auto [first, second]), GetPair()); // ``` // // Attempting to assign to a ternary will not compile: // ``` // ASSIGN_OR_RETURN((cond ? a : b), MaybeGetValue(arg)); // DOES NOT COMPILE // ``` // // Adjusting the returned error: // ``` // ASSIGN_OR_RETURN(ValueType value, MaybeGetValue(query), // [](const auto& e) { // return base::StrCat({e, " while getting value"}); // }); // ``` // // Returning a different kind of error: // ``` // ASSIGN_OR_RETURN(ValueType value, MaybeGetValue(query), // [](auto) { return SomeErrorCode::kFail); }); // ``` // // Returning void: // ``` // ASSIGN_OR_RETURN(ValueType value, MaybeGetValue(query), [](auto) {}); // ``` // ``` // ASSIGN_OR_RETURN(ValueType value, MaybeGetValue(query), // [](auto) { LOG(WARNING) << "Uh oh"; }()); // ``` // // Automatic conversion to `base::expected<U, E>`: // ``` // base::expected<int, SomeErrorCode> Foo() { // ASSIGN_OR_RETURN(ValueType value, MaybeGetValue(query), // [](auto) { return SomeErrorCode::kFail); }); // return 17; // } // ``` // // Passing the error to a static/global handler: // ``` // ASSIGN_OR_RETURN(ValueType value, MaybeGetValue(query), &FailureHandler); // ``` // // Passing the error to a handler member function: // ``` // ASSIGN_OR_RETURN(ValueType value, MaybeGetValue(query), // &MyClass::FailureHandler, this); // ``` #define ASSIGN_OR_RETURN(lhs, rexpr, ...) … namespace base::internal { // ================================================================= // == Implementation details, do not rely on anything below here. == // ================================================================= // Helper object to allow returning some `E` from a method either directly or in // the error of an `expected<T, E>`. Supports move-only `E`, as well as `void`. // // In order to support `void` return types, `UnexpectedDeducer` is not // constructed directly from an `E`, but from a lambda that returns `E`; and // callers must return `Ret()` rather than returning the deducer itself. Using // both these indirections allows consistent invocation from macros. template <typename Lambda, typename E = std::invoke_result_t<Lambda&&>> class UnexpectedDeducer { public: constexpr explicit UnexpectedDeducer(Lambda&& lambda) noexcept : … { … } constexpr decltype(auto) Ret() && noexcept { … } // Allow implicit conversion from `Ret()` to either `expected<T, E>` (for // arbitrary `T`) or `E`. template <typename T> // NOLINTNEXTLINE(google-explicit-constructor) constexpr operator expected<T, E>() && noexcept { … } // NOLINTNEXTLINE(google-explicit-constructor) constexpr operator E() && noexcept { return std::move(lambda_)(); } private: // RAW_PTR_EXCLUSION: Not intended to handle &&-qualified members. // `UnexpectedDeducer` is a short-lived temporary and tries to minimize // copying and other overhead; using raw_ptr/ref goes against this design // without adding meaningful safety. RAW_PTR_EXCLUSION Lambda&& lambda_; }; // Deduce the type of the lambda automatically so callers don't need to spell // things twice (or use temps) and use decltype. template <typename Lambda> UnexpectedDeducer(Lambda) -> UnexpectedDeducer<Lambda>; } // namespace base::internal #define BASE_INTERNAL_EXPECTED_BODY(expected, rexpr, name, return_keyword, \ error_expr) … #define BASE_INTERNAL_EXPECTED_RETURN_IF_ERROR(expected, rexpr, \ return_keyword, error_expr) … #define BASE_INTERNAL_EXPECTED_ASSIGN_OR_RETURN( \ expected, rexpr, return_keyword, error_expr, lhs) … #define BASE_INTERNAL_EXPECTED_PASS_ARGS(func, ...) … // These are necessary to avoid mismatched parens inside __VA_OPT__() below. #define BASE_INTERNAL_EXPECTED_BEGIN_INVOKE … #define BASE_INTERNAL_EXPECTED_END_INVOKE … #define BASE_INTERNAL_EXPECTED_ARGS(temp_name, return_keyword, rexpr, ...) … #define BASE_INTERNAL_EXPECTED_RETURN_IF_ERROR_IMPL(return_keyword, rexpr, \ ...) … #define BASE_INTERNAL_EXPECTED_ASSIGN_OR_RETURN_IMPL(return_keyword, lhs, \ rexpr, ...) … #endif // BASE_TYPES_EXPECTED_MACROS_H_