chromium/base/types/expected_macros.h

// 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_