Add parentheses-removal support for non-__VA_OPT__-supporting compilers to status macros This enables writing expressions such as ABSL_ASSIGN_OR_RETURN((std::tuple<int, int> t1), ...) to allow commas in the type name on MSVC's traditional preprocessor, which does not support __VA_OPT__. PiperOrigin-RevId: 919705428 Change-Id: I6887b5607d422b8bc4586068ed42b4e9d384ee44
diff --git a/absl/status/status_macros.h b/absl/status/status_macros.h index 96c97b8..cfa9b09 100644 --- a/absl/status/status_macros.h +++ b/absl/status/status_macros.h
@@ -266,52 +266,67 @@ // Internal helpers for macro expansion. #define ABSL_INTERNAL_STATUS_MACROS_IMPL_EAT(...) #define ABSL_INTERNAL_STATUS_MACROS_IMPL_REM(...) __VA_ARGS__ -#define ABSL_INTERNAL_STATUS_MACROS_IMPL_EMPTY() +#define ABSL_INTERNAL_STATUS_MACROS_IMPL_COMMA(...) , +#define ABSL_INTERNAL_STATUS_MACROS_IMPL_ARG_3(a, b, c, ...) c -// Internal helpers for emptiness arguments check. -#define ABSL_INTERNAL_STATUS_MACROS_IMPL_IS_EMPTY_INNER(...) \ - ABSL_INTERNAL_STATUS_MACROS_IMPL_IS_EMPTY_INNER_HELPER((__VA_ARGS__, 0, 1)) -// MSVC expands variadic macros incorrectly, so we need this extra indirection -// to work around that (b/110959038). -#define ABSL_INTERNAL_STATUS_MACROS_IMPL_IS_EMPTY_INNER_HELPER(args) \ - ABSL_INTERNAL_STATUS_MACROS_IMPL_IS_EMPTY_INNER_I args -#define ABSL_INTERNAL_STATUS_MACROS_IMPL_IS_EMPTY_INNER_I(e0, e1, is_empty, \ - ...) \ - is_empty +#define ABSL_INTERNAL_STATUS_MACROS_IMPL_HAS_COMMA(...) \ + ABSL_INTERNAL_STATUS_MACROS_IMPL_REM( \ + ABSL_INTERNAL_STATUS_MACROS_IMPL_ARG_3(__VA_ARGS__, 1, 10)) -#define ABSL_INTERNAL_STATUS_MACROS_IMPL_IS_EMPTY(...) \ - ABSL_INTERNAL_STATUS_MACROS_IMPL_IS_EMPTY_I(__VA_ARGS__) +// Concatenates five macro arguments. +#define ABSL_INTERNAL_STATUS_MACROS_IMPL_CONCAT_5(a, b, c, d, e) a##b##c##d##e -#if !defined(_MSC_VER) || defined(__clang__) || \ - (defined(_MSVC_TRADITIONAL) && !_MSVC_TRADITIONAL) -#define ABSL_INTERNAL_STATUS_MACROS_IMPL_IS_EMPTY_I(...) \ - ABSL_INTERNAL_STATUS_MACROS_IMPL_IS_EMPTY_INNER(_ __VA_OPT__(, )##__VA_ARGS__) +// Identifies (below) the case where all arguments are empty, except the last. +#define ABSL_INTERNAL_STATUS_MACROS_IMPL_IS_EMPTY_CASE_1010101 , + +// Evaluates to 1 if the arguments concatenate to 1010101, and 10 otherwise. +#define ABSL_INTERNAL_STATUS_MACROS_1_IF_1010101_ELSE_10(a, b, c, d) \ + ABSL_INTERNAL_STATUS_MACROS_IMPL_HAS_COMMA( \ + ABSL_INTERNAL_STATUS_MACROS_IMPL_CONCAT_5( \ + ABSL_INTERNAL_STATUS_MACROS_IMPL_IS_EMPTY_CASE_, a, b, c, d)) + +// Evaluates to 1 if __VA_OPT__ is supported and the argument list is non-empty, +// and 0 otherwise. +#define ABSL_INTERNAL_STATUS_MACROS_IMPL_HAVE_VA_OPT(...) \ + ABSL_INTERNAL_STATUS_MACROS_IMPL_ARG_3(__VA_OPT__(, ), 1, 0, ) + +#if ABSL_INTERNAL_STATUS_MACROS_IMPL_HAVE_VA_OPT(.) +// Evaluates to 1 if the argument list is empty, and 10 otherwise. +#define ABSL_INTERNAL_STATUS_MACROS_1_IF_EMPTY_ELSE_10(...) 1##__VA_OPT__(0) #else -// Nonstandard hack that works for MSVC's legacy preprocessor -#define ABSL_INTERNAL_STATUS_MACROS_IMPL_IS_EMPTY_I(...) \ - ABSL_INTERNAL_STATUS_MACROS_IMPL_IS_EMPTY_INNER(_, ##__VA_ARGS__) +#define ABSL_INTERNAL_STATUS_MACROS_1_IF_EMPTY_ELSE_10(...) \ + ABSL_INTERNAL_STATUS_MACROS_1_IF_1010101_ELSE_10( \ + ABSL_INTERNAL_STATUS_MACROS_IMPL_HAS_COMMA(__VA_ARGS__), \ + ABSL_INTERNAL_STATUS_MACROS_IMPL_HAS_COMMA( \ + ABSL_INTERNAL_STATUS_MACROS_IMPL_COMMA __VA_ARGS__), \ + ABSL_INTERNAL_STATUS_MACROS_IMPL_HAS_COMMA(__VA_ARGS__()), \ + ABSL_INTERNAL_STATUS_MACROS_IMPL_HAS_COMMA( \ + ABSL_INTERNAL_STATUS_MACROS_IMPL_COMMA __VA_ARGS__())) + #endif -// Internal helpers for if statement. -#define ABSL_INTERNAL_STATUS_MACROS_IMPL_IF_1(_Then, _Else) _Then -#define ABSL_INTERNAL_STATUS_MACROS_IMPL_IF_0(_Then, _Else) _Else -#define ABSL_INTERNAL_STATUS_MACROS_IMPL_IF(_Cond, _Then, _Else) \ - ABSL_INTERNAL_STATUS_MACROS_IMPL_CONCAT_( \ - ABSL_INTERNAL_STATUS_MACROS_IMPL_IF_, _Cond)(_Then, _Else) +// Parenthesized case: strips the initial pair of parentheses from the input. +// +// Note that this may not result in an equivalent expression. For example, +// (*a)[b] would evaluate to a[b]. The caller below is required to ensure +// this is only called when the input is fully parenthesized to avoid this +// issue. +#define ABSL_INTERNAL_STATUS_MACROS_IMPL_UNPARENTHESIZE_IF_PARENTHESIZED_1(x) \ + ABSL_INTERNAL_STATUS_MACROS_IMPL_REM x -// Expands to 1 if the input is parenthesized. Otherwise expands to 0. -#define ABSL_INTERNAL_STATUS_MACROS_IMPL_IS_PARENTHESIZED(...) \ - ABSL_INTERNAL_STATUS_MACROS_IMPL_IS_EMPTY( \ - ABSL_INTERNAL_STATUS_MACROS_IMPL_EAT __VA_ARGS__) +// Unparenthesized case: evaluates to the argument list as-is. +#define ABSL_INTERNAL_STATUS_MACROS_IMPL_UNPARENTHESIZE_IF_PARENTHESIZED_10(x) \ + ABSL_INTERNAL_STATUS_MACROS_IMPL_REM(x) // If the input is parenthesized, removes the parentheses. Otherwise expands to // the input unchanged. #define ABSL_INTERNAL_STATUS_MACROS_IMPL_UNPARENTHESIZE_IF_PARENTHESIZED(...) \ - ABSL_INTERNAL_STATUS_MACROS_IMPL_IF( \ - ABSL_INTERNAL_STATUS_MACROS_IMPL_IS_PARENTHESIZED(__VA_ARGS__), \ - ABSL_INTERNAL_STATUS_MACROS_IMPL_REM, \ - ABSL_INTERNAL_STATUS_MACROS_IMPL_EMPTY()) \ - __VA_ARGS__ + ABSL_INTERNAL_STATUS_MACROS_IMPL_REM( \ + ABSL_INTERNAL_STATUS_MACROS_IMPL_CONCAT_( \ + ABSL_INTERNAL_STATUS_MACROS_IMPL_UNPARENTHESIZE_IF_PARENTHESIZED_, \ + ABSL_INTERNAL_STATUS_MACROS_1_IF_EMPTY_ELSE_10( \ + ABSL_INTERNAL_STATUS_MACROS_IMPL_EAT __VA_ARGS__))( \ + ABSL_INTERNAL_STATUS_MACROS_IMPL_REM(__VA_ARGS__))) // Internal helper for concatenating macro values. #define ABSL_INTERNAL_STATUS_MACROS_IMPL_CONCAT_INNER_(x, y) x##y
diff --git a/absl/status/status_macros_test.cc b/absl/status/status_macros_test.cc index 7aaeb43..01ebdbf 100644 --- a/absl/status/status_macros_test.cc +++ b/absl/status/status_macros_test.cc
@@ -136,9 +136,6 @@ } TEST(AssignOrReturn, WorksWithCommasInType) { -#if defined(_MSC_VER) && (!defined(_MSVC_TRADITIONAL) || _MSVC_TRADITIONAL) - GTEST_SKIP() << "Comma support on MSVC requires /Zc:preprocessor"; -#else auto func = []() -> absl::Status { ABSL_ASSIGN_OR_RETURN((std::tuple<int, int> t1), ReturnStatusOrTupleValue(1, 1)); @@ -154,13 +151,9 @@ }; EXPECT_THAT(func().message(), Eq("EXPECTED")); -#endif } TEST(AssignOrReturn, WorksWithStructureBindings) { -#if defined(_MSC_VER) && (!defined(_MSVC_TRADITIONAL) || _MSVC_TRADITIONAL) - GTEST_SKIP() << "Comma support on MSVC requires /Zc:preprocessor"; -#else auto func = []() -> absl::Status { ABSL_ASSIGN_OR_RETURN( (const auto& [t1, t2, t3, t4, t5]), @@ -176,13 +169,9 @@ }; EXPECT_THAT(func().message(), Eq("EXPECTED")); -#endif } TEST(AssignOrReturn, WorksWithParenthesesAndDereference) { -#if defined(_MSC_VER) && (!defined(_MSVC_TRADITIONAL) || _MSVC_TRADITIONAL) - GTEST_SKIP() << "Comma support on MSVC requires /Zc:preprocessor"; -#else auto func = []() -> absl::Status { int integer; int* pointer_to_integer = &integer; @@ -203,7 +192,6 @@ }; EXPECT_THAT(func().message(), Eq("EXPECTED")); -#endif } TEST(AssignOrReturn, WorksWithAppend) { @@ -245,9 +233,6 @@ } TEST(AssignOrReturn, WorksWithThirdArgumentAndCommas) { -#if defined(_MSC_VER) && (!defined(_MSVC_TRADITIONAL) || _MSVC_TRADITIONAL) - GTEST_SKIP() << "Comma support on MSVC requires /Zc:preprocessor"; -#else auto fail_test_if_called = [](absl::StatusBuilder builder) { ADD_FAILURE(); return builder; @@ -274,7 +259,6 @@ EXPECT_THAT(func().message(), AllOf(HasSubstr("EXPECTED A"), HasSubstr("EXPECTED B"))); -#endif } TEST(AssignOrReturn, WorksWithAppendIncludingLocals) {