Precursor changes to minimize diff for optional support.
* Comment tweaks
* A few appropriate-seeming constraints
* Better way of invoking invoke()
* More test cases
* Be consistent about (actual, expected) order
Bug: none
Change-Id: I4725dcc647f8727952f997c6b334d1476656be75
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6192027
Auto-Submit: Peter Kasting <pkasting@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Code-Coverage: findit-for-me@appspot.gserviceaccount.com <findit-for-me@appspot.gserviceaccount.com>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1409958}
diff --git a/base/types/expected_macros.h b/base/types/expected_macros.h
index eea12b2c..50c6ac5 100644
--- a/base/types/expected_macros.h
+++ b/base/types/expected_macros.h
@@ -12,18 +12,20 @@
#include "base/compiler_specific.h"
#include "base/macros/concat.h"
+#include "base/macros/if.h"
+#include "base/macros/is_empty.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
+// Executes an expression `rexpr` that returns an `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)`.
+// result of `std::invoke(..., E)` on the additional arguments.
//
// 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
@@ -96,10 +98,10 @@
BASE_UNIQUIFY(_expected_value), rexpr, \
__VA_ARGS__)
-// 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.
+// Executes an expression `rexpr` that returns an `expected<T, E>`. If the
+// result is not an error, 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
//
@@ -209,7 +211,7 @@
// 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
+// constructed directly with an `E`, but with 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&&>>
@@ -230,11 +232,17 @@
// arbitrary `T`) or `E`.
template <typename T>
// NOLINTNEXTLINE(google-explicit-constructor)
- constexpr operator expected<T, E>() && noexcept {
+ constexpr operator expected<T, E>() && noexcept
+ requires(!std::is_void_v<E>)
+ {
return expected<T, E>(unexpect, std::move(lambda_)());
}
// NOLINTNEXTLINE(google-explicit-constructor)
- constexpr operator E() && noexcept { return std::move(lambda_)(); }
+ constexpr operator E() && noexcept
+ requires(!std::is_void_v<E>)
+ {
+ return std::move(lambda_)();
+ }
private:
// RAW_PTR_EXCLUSION: Not intended to handle &&-qualified members.
@@ -253,23 +261,19 @@
#define BASE_INTERNAL_EXPECTED_PASS_ARGS(func, ...) func(__VA_ARGS__)
-// These are necessary to avoid mismatched parens inside __VA_OPT__() below.
-#define BASE_INTERNAL_EXPECTED_BEGIN_INVOKE std::invoke(
-#define BASE_INTERNAL_EXPECTED_END_INVOKE )
-
-#define BASE_INTERNAL_EXPECTED_BODY(expected, rexpr, name, ...) \
- decltype(auto) expected = (rexpr); \
- { \
- static_assert(base::internal::IsExpected<decltype(expected)>, \
- #name " should only be used with base::expected<>"); \
- } \
- if (!expected.has_value()) [[unlikely]] { \
- return base::internal::UnexpectedDeducer([&] { \
- return __VA_OPT__(BASE_INTERNAL_EXPECTED_BEGIN_INVOKE) \
- __VA_ARGS__ __VA_OPT__(, ) std::move(expected) \
- .error() __VA_OPT__(BASE_INTERNAL_EXPECTED_END_INVOKE); \
- }) \
- .Ret(); \
+#define BASE_INTERNAL_EXPECTED_BODY(expected, rexpr, name, ...) \
+ decltype(auto) expected = (rexpr); \
+ { \
+ static_assert(base::internal::IsExpected<decltype(expected)>, \
+ #name " should only be used with base::expected<>"); \
+ } \
+ if (!expected.has_value()) [[unlikely]] { \
+ return base::internal::UnexpectedDeducer([&] { \
+ return BASE_IF( \
+ BASE_IS_EMPTY(__VA_ARGS__), std::move(expected).error(), \
+ std::invoke(__VA_ARGS__, std::move(expected).error())); \
+ }) \
+ .Ret(); \
}
#define BASE_INTERNAL_EXPECTED_RETURN_IF_ERROR(expected, rexpr, ...) \
@@ -279,7 +283,6 @@
} while (false)
#define BASE_INTERNAL_EXPECTED_ASSIGN_OR_RETURN(lhs, expected, rexpr, ...) \
- BASE_INTERNAL_EXPECTED_BODY(expected, rexpr, ASSIGN_OR_RETURN, __VA_ARGS__); \
{ \
constexpr auto lhs_v = std::string_view(#lhs); \
static_assert(!(lhs_v.front() == '(' && lhs_v.back() == ')' && \
@@ -288,6 +291,7 @@
"parenthesized expressions containing '?' to the first " \
"argument of ASSIGN_OR_RETURN()"); \
} \
+ BASE_INTERNAL_EXPECTED_BODY(expected, rexpr, ASSIGN_OR_RETURN, __VA_ARGS__); \
BASE_REMOVE_PARENS(lhs) = std::move(expected).value();
#endif // BASE_TYPES_EXPECTED_MACROS_H_
diff --git a/base/types/expected_macros_unittest.cc b/base/types/expected_macros_unittest.cc
index 695f84d0..70fc026 100644
--- a/base/types/expected_macros_unittest.cc
+++ b/base/types/expected_macros_unittest.cc
@@ -57,12 +57,15 @@
TEST(ReturnIfError, Works) {
const auto func = []() -> std::string {
RETURN_IF_ERROR(ReturnOk());
- RETURN_IF_ERROR(ReturnOk());
+ expected<int, std::string> e_int1 = 1;
+ RETURN_IF_ERROR(e_int1);
+ const expected<int, std::string> e_int2 = 2;
+ RETURN_IF_ERROR(e_int2);
RETURN_IF_ERROR(ReturnError("EXPECTED"));
return "ERROR";
};
- EXPECT_EQ("EXPECTED", func());
+ EXPECT_EQ(func(), "EXPECTED");
}
TEST(ReturnIfError, WorksWithExpectedReturn) {
@@ -83,7 +86,7 @@
return "ERROR";
};
- EXPECT_EQ("EXPECTED", func());
+ EXPECT_EQ(func(), "EXPECTED");
}
TEST(ReturnIfError, WorksWithMoveOnlyType) {
@@ -116,7 +119,7 @@
return "ERROR";
};
- EXPECT_EQ("EXPECTED A EXPECTED B", func());
+ EXPECT_EQ(func(), "EXPECTED A EXPECTED B");
}
TEST(ReturnIfError, WorksWithAdaptorFuncAndExpectedReturn) {
@@ -173,27 +176,33 @@
TEST(AssignOrReturn, Works) {
const auto func = []() -> std::string {
ASSIGN_OR_RETURN(int value1, ReturnValue(1));
- EXPECT_EQ(1, value1);
+ EXPECT_EQ(value1, 1);
ASSIGN_OR_RETURN(const int value2, ReturnValue(2));
- EXPECT_EQ(2, value2);
+ EXPECT_EQ(value2, 2);
ASSIGN_OR_RETURN(const int& value3, ReturnValue(3));
- EXPECT_EQ(3, value3);
- ASSIGN_OR_RETURN([[maybe_unused]] const int value4,
+ EXPECT_EQ(value3, 3);
+ expected<int, std::string> e_int4 = 4;
+ ASSIGN_OR_RETURN(int value4, e_int4);
+ EXPECT_EQ(value4, 4);
+ const expected<int, std::string> e_int5 = 5;
+ ASSIGN_OR_RETURN(int value5, e_int5);
+ EXPECT_EQ(value5, 5);
+ ASSIGN_OR_RETURN([[maybe_unused]] const int value6,
ReturnError("EXPECTED"));
return "ERROR";
};
- EXPECT_EQ("EXPECTED", func());
+ EXPECT_EQ(func(), "EXPECTED");
}
TEST(AssignOrReturn, WorksWithExpectedReturn) {
const auto func = []() -> expected<void, std::string> {
ASSIGN_OR_RETURN(int value1, ReturnValue(1));
- EXPECT_EQ(1, value1);
+ EXPECT_EQ(value1, 1);
ASSIGN_OR_RETURN(const int value2, ReturnValue(2));
- EXPECT_EQ(2, value2);
+ EXPECT_EQ(value2, 2);
ASSIGN_OR_RETURN(const int& value3, ReturnValue(3));
- EXPECT_EQ(3, value3);
+ EXPECT_EQ(value3, 3);
ASSIGN_OR_RETURN([[maybe_unused]] const int value4,
ReturnError("EXPECTED"));
return ok();
@@ -205,13 +214,13 @@
TEST(AssignOrReturn, WorksWithLambda) {
const auto func = []() -> std::string {
ASSIGN_OR_RETURN(const int value1, [] { return ReturnValue(1); }());
- EXPECT_EQ(1, value1);
+ EXPECT_EQ(value1, 1);
ASSIGN_OR_RETURN([[maybe_unused]] const int value2,
[] { return ReturnError("EXPECTED"); }());
return "ERROR";
};
- EXPECT_EQ("EXPECTED", func());
+ EXPECT_EQ(func(), "EXPECTED");
}
TEST(AssignOrReturn, WorksWithMoveOnlyType) {
@@ -237,33 +246,33 @@
TEST(AssignOrReturn, WorksWithCommasInType) {
const auto func = []() -> std::string {
ASSIGN_OR_RETURN((const std::tuple<int, int> t1), ReturnTupleValue(1, 1));
- EXPECT_EQ((std::tuple(1, 1)), t1);
+ EXPECT_EQ(t1, (std::tuple(1, 1)));
ASSIGN_OR_RETURN((const std::tuple<int, std::tuple<int, int>, int> t2),
ReturnTupleValue(1, std::tuple(1, 1), 1));
- EXPECT_EQ((std::tuple(1, std::tuple(1, 1), 1)), t2);
+ EXPECT_EQ(t2, (std::tuple(1, std::tuple(1, 1), 1)));
ASSIGN_OR_RETURN(
([[maybe_unused]] const std::tuple<int, std::tuple<int, int>, int> t3),
(ReturnTupleError<int, std::tuple<int, int>, int>("EXPECTED")));
return "ERROR";
};
- EXPECT_EQ("EXPECTED", func());
+ EXPECT_EQ(func(), "EXPECTED");
}
TEST(AssignOrReturn, WorksWithStructuredBindings) {
const auto func = []() -> std::string {
ASSIGN_OR_RETURN((const auto& [t1, t2, t3, t4, t5]),
ReturnTupleValue(std::tuple(1, 1), 1, 2, 3, 4));
- EXPECT_EQ((std::tuple(1, 1)), t1);
- EXPECT_EQ(1, t2);
- EXPECT_EQ(2, t3);
- EXPECT_EQ(3, t4);
- EXPECT_EQ(4, t5);
+ EXPECT_EQ(t1, (std::tuple(1, 1)));
+ EXPECT_EQ(t2, 1);
+ EXPECT_EQ(t3, 2);
+ EXPECT_EQ(t4, 3);
+ EXPECT_EQ(t5, 4);
ASSIGN_OR_RETURN([[maybe_unused]] int t6, ReturnError("EXPECTED"));
return "ERROR";
};
- EXPECT_EQ("EXPECTED", func());
+ EXPECT_EQ(func(), "EXPECTED");
}
TEST(AssignOrReturn, WorksWithParenthesesAndDereference) {
@@ -271,19 +280,19 @@
int integer;
int* pointer_to_integer = &integer;
ASSIGN_OR_RETURN((*pointer_to_integer), ReturnValue(1));
- EXPECT_EQ(1, integer);
+ EXPECT_EQ(integer, 1);
ASSIGN_OR_RETURN(*pointer_to_integer, ReturnValue(2));
- EXPECT_EQ(2, integer);
+ EXPECT_EQ(integer, 2);
// Test where the order of dereference matters.
- pointer_to_integer--;
+ --pointer_to_integer;
int* const* const pointer_to_pointer_to_integer = &pointer_to_integer;
ASSIGN_OR_RETURN((*pointer_to_pointer_to_integer)[1], ReturnValue(3));
- EXPECT_EQ(3, integer);
+ EXPECT_EQ(integer, 3);
ASSIGN_OR_RETURN([[maybe_unused]] const int t1, ReturnError("EXPECTED"));
return "ERROR";
};
- EXPECT_EQ("EXPECTED", func());
+ EXPECT_EQ(func(), "EXPECTED");
}
TEST(AssignOrReturn, WorksWithAdaptorFunc) {
@@ -293,13 +302,13 @@
};
const auto adaptor = [](std::string error) { return error + " EXPECTED B"; };
const auto func = [&]() -> std::string {
- ASSIGN_OR_RETURN([[maybe_unused]] int value, ReturnValue(1),
- fail_test_if_called);
+ ASSIGN_OR_RETURN(int value, ReturnValue(1), fail_test_if_called);
+ EXPECT_EQ(value, 1);
ASSIGN_OR_RETURN(value, ReturnError("EXPECTED A"), adaptor);
return "ERROR";
};
- EXPECT_EQ("EXPECTED A EXPECTED B", func());
+ EXPECT_EQ(func(), "EXPECTED A EXPECTED B");
}
TEST(AssignOrReturn, WorksWithAdaptorFuncAndExpectedReturn) {
@@ -372,7 +381,7 @@
return "ERROR";
};
- EXPECT_EQ("EXPECTED A EXPECTED B", func());
+ EXPECT_EQ(func(), "EXPECTED A EXPECTED B");
}
TEST(AssignOrReturn, WorksWithAppendIncludingLocals) {
@@ -383,21 +392,21 @@
return "ERROR";
};
- EXPECT_EQ("EXPECTED A EXPECTED B", func(" EXPECTED B"));
+ EXPECT_EQ(func(" EXPECTED B"), "EXPECTED A EXPECTED B");
}
TEST(AssignOrReturn, WorksForExistingVariable) {
const auto func = []() -> std::string {
int value = 1;
ASSIGN_OR_RETURN(value, ReturnValue(2));
- EXPECT_EQ(2, value);
+ EXPECT_EQ(value, 2);
ASSIGN_OR_RETURN(value, ReturnValue(3));
- EXPECT_EQ(3, value);
+ EXPECT_EQ(value, 3);
ASSIGN_OR_RETURN(value, ReturnError("EXPECTED"));
return "ERROR";
};
- EXPECT_EQ("EXPECTED", func());
+ EXPECT_EQ(func(), "EXPECTED");
}
TEST(AssignOrReturn, UniquePtrWorks) {
@@ -407,7 +416,7 @@
return "EXPECTED";
};
- EXPECT_EQ("EXPECTED", func());
+ EXPECT_EQ(func(), "EXPECTED");
}
TEST(AssignOrReturn, UniquePtrWorksForExistingVariable) {
@@ -421,7 +430,7 @@
return "EXPECTED";
};
- EXPECT_EQ("EXPECTED", func());
+ EXPECT_EQ(func(), "EXPECTED");
}
} // namespace