From 6bf3c73fdfeb62733d2a0f81b9846ff77f3a3b9f Mon Sep 17 00:00:00 2001 From: Vitaly Goldshteyn Date: Wed, 6 Mar 2024 02:38:50 -0800 Subject: Document and test ability to use absl::Overload with generic lambdas. PiperOrigin-RevId: 613141617 Change-Id: Ife68495d4a5951b734db7407fb99686cf45a5133 --- absl/functional/overload_test.cc | 44 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) (limited to 'absl/functional/overload_test.cc') diff --git a/absl/functional/overload_test.cc b/absl/functional/overload_test.cc index 739c4c4c..040bf83f 100644 --- a/absl/functional/overload_test.cc +++ b/absl/functional/overload_test.cc @@ -30,7 +30,7 @@ namespace { -TEST(OverloadTest, DispatchConsidersType) { +TEST(OverloadTest, DispatchConsidersTypeWithAutoFallback) { auto overloaded = absl::Overload( [](int v) -> std::string { return absl::StrCat("int ", v); }, // [](double v) -> std::string { return absl::StrCat("double ", v); }, // @@ -103,6 +103,35 @@ TEST(OverloadTest, AmbiguousConversionNotInvocable) { static_assert(!std::is_invocable_v); } +TEST(OverloadTest, AmbiguousConversionWithAutoNotInvocable) { + auto overloaded = absl::Overload( // + [](auto a) { return a; }, // + [](auto c) { return c; } // + ); + static_assert(!std::is_invocable_v); +} + +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L + +TEST(OverloadTest, AmbiguousConversionWithAutoAndTemplateNotInvocable) { + auto overloaded = absl::Overload( // + [](auto a) { return a; }, // + [](T c) { return c; } // + ); + static_assert(!std::is_invocable_v); +} + +TEST(OverloadTest, DispatchConsidersTypeWithTemplateFallback) { + auto overloaded = absl::Overload( // + [](int a) { return a; }, // + [](T c) { return c * 2; } // + ); + EXPECT_EQ(7, overloaded(7)); + EXPECT_EQ(14.0, overloaded(7.0)); +} + +#endif // ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L + TEST(OverloadTest, DispatchConsidersSfinae) { auto overloaded = absl::Overload( // [](auto a) -> decltype(a + 1) { return a + 1; } // @@ -125,6 +154,19 @@ TEST(OverloadTest, VariantVisitDispatchesCorrectly) { EXPECT_EQ("string", absl::visit(overloaded, v)); } +TEST(OverloadTest, VariantVisitWithAutoFallbackDispatchesCorrectly) { + absl::variant v(int32_t{1}); + auto overloaded = absl::Overload( + [](const std::string& s) { return s.size(); }, // + [](const auto& s) { return sizeof(s); } // + ); + EXPECT_EQ(4, absl::visit(overloaded, v)); + v = int64_t{1}; + EXPECT_EQ(8, absl::visit(overloaded, v)); + v = std::string("hello"); + EXPECT_EQ(5, absl::visit(overloaded, v)); +} + } // namespace #endif -- cgit v1.2.3 From e022c806b07c661932967a2680e32cafdf572895 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 23 Apr 2024 18:29:14 -0700 Subject: overload: allow using the underlying type with CTAD directly. There's no need to go through an extra layer of indirection to get this. PiperOrigin-RevId: 627567108 Change-Id: I241851c5294417aae069047e35961ed58475e59e --- absl/functional/overload.h | 66 ++++++++++-------- absl/functional/overload_test.cc | 145 +++++++++++++++++++++++---------------- 2 files changed, 125 insertions(+), 86 deletions(-) (limited to 'absl/functional/overload_test.cc') diff --git a/absl/functional/overload.h b/absl/functional/overload.h index 732795fc..34ffa689 100644 --- a/absl/functional/overload.h +++ b/absl/functional/overload.h @@ -16,33 +16,26 @@ // File: overload.h // ----------------------------------------------------------------------------- // -// `absl::Overload()` returns a functor that provides overloads based on the -// functors passed to it. +// `absl::Overload` is a functor that provides overloads based on the functors +// with which it is created. This can, for example, be used to locally define an +// anonymous visitor type for `std::visit` inside a function using lambdas. +// // Before using this function, consider whether named function overloads would // be a better design. -// One use case for this is locally defining visitors for `std::visit` inside a -// function using lambdas. - -// Example: Using `absl::Overload` to define a visitor for `std::variant`. -// -// std::variant v(int{1}); // -// assert(std::visit(absl::Overload( -// [](int) -> absl::string_view { return "int"; }, -// [](const std::string&) -> absl::string_view { -// return "string"; -// }, -// [](double) -> absl::string_view { return "double"; }), -// v) == "int"); +// Note: absl::Overload requires C++17. // -// One of the lambda may specify overload for several types via generic lambda. +// Example: // -// absl::variant v(int32_t{1}); -// assert(std::visit(absl::Overload( -// [](const std::string& s) { return s.size(); }, -// [](const auto& s) { return sizeof(s); }), v) == 4); +// std::variant v(int32_t{1}); +// const size_t result = +// std::visit(absl::Overload{ +// [](const std::string& s) { return s.size(); }, +// [](const auto& s) { return sizeof(s); }, +// }, +// v); +// assert(result == 4); // -// Note: absl::Overload requires C++17. #ifndef ABSL_FUNCTIONAL_OVERLOAD_H_ #define ABSL_FUNCTIONAL_OVERLOAD_H_ @@ -56,14 +49,30 @@ ABSL_NAMESPACE_BEGIN #if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \ ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L -template -auto Overload(T&&... ts) { - struct OverloadImpl : absl::remove_cvref_t... { - using absl::remove_cvref_t::operator()...; - }; - return OverloadImpl{std::forward(ts)...}; -} +template +struct Overload final : T... { + using T::operator()...; + + // For historical reasons we want to support use that looks like a function + // call: + // + // absl::Overload(lambda_1, lambda_2) + // + // This works automatically in C++20 because we have support for parenthesized + // aggregate initialization. Before then we must provide a constructor that + // makes this work. + // + explicit Overload(T... ts) : T(std::move(ts))... {} +}; + +// Before C++20, which added support for CTAD for aggregate types, we must also +// teach the compiler how to deduce the template arguments for Overload. +// +template +Overload(T...) -> Overload; + #else + namespace functional_internal { template constexpr bool kDependentFalse = false; @@ -76,6 +85,7 @@ auto Overload(T&&...) { } #endif + ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/functional/overload_test.cc b/absl/functional/overload_test.cc index 040bf83f..a2e87674 100644 --- a/absl/functional/overload_test.cc +++ b/absl/functional/overload_test.cc @@ -31,14 +31,15 @@ namespace { TEST(OverloadTest, DispatchConsidersTypeWithAutoFallback) { - auto overloaded = absl::Overload( - [](int v) -> std::string { return absl::StrCat("int ", v); }, // - [](double v) -> std::string { return absl::StrCat("double ", v); }, // - [](const char* v) -> std::string { // - return absl::StrCat("const char* ", v); // - }, // - [](auto v) -> std::string { return absl::StrCat("auto ", v); } // - ); + auto overloaded = absl::Overload{ + [](int v) -> std::string { return absl::StrCat("int ", v); }, + [](double v) -> std::string { return absl::StrCat("double ", v); }, + [](const char* v) -> std::string { + return absl::StrCat("const char* ", v); + }, + [](auto v) -> std::string { return absl::StrCat("auto ", v); }, + }; + EXPECT_EQ("int 1", overloaded(1)); EXPECT_EQ("double 2.5", overloaded(2.5)); EXPECT_EQ("const char* hello", overloaded("hello")); @@ -46,32 +47,34 @@ TEST(OverloadTest, DispatchConsidersTypeWithAutoFallback) { } TEST(OverloadTest, DispatchConsidersNumberOfArguments) { - auto overloaded = absl::Overload( // - [](int a) { return a + 1; }, // - [](int a, int b) { return a * b; }, // - []() -> absl::string_view { return "none"; } // - ); + auto overloaded = absl::Overload{ + [](int a) { return a + 1; }, + [](int a, int b) { return a * b; }, + []() -> absl::string_view { return "none"; }, + }; + EXPECT_EQ(3, overloaded(2)); EXPECT_EQ(21, overloaded(3, 7)); EXPECT_EQ("none", overloaded()); } TEST(OverloadTest, SupportsConstantEvaluation) { - auto overloaded = absl::Overload( // - [](int a) { return a + 1; }, // - [](int a, int b) { return a * b; }, // - []() -> absl::string_view { return "none"; } // - ); + auto overloaded = absl::Overload{ + [](int a) { return a + 1; }, + [](int a, int b) { return a * b; }, + []() -> absl::string_view { return "none"; }, + }; + static_assert(overloaded() == "none"); static_assert(overloaded(2) == 3); static_assert(overloaded(3, 7) == 21); } TEST(OverloadTest, PropogatesDefaults) { - auto overloaded = absl::Overload( // - [](int a, int b = 5) { return a * b; }, // - [](double c) { return c; } // - ); + auto overloaded = absl::Overload{ + [](int a, int b = 5) { return a * b; }, + [](double c) { return c; }, + }; EXPECT_EQ(21, overloaded(3, 7)); EXPECT_EQ(35, overloaded(7)); @@ -79,53 +82,59 @@ TEST(OverloadTest, PropogatesDefaults) { } TEST(OverloadTest, AmbiguousWithDefaultsNotInvocable) { - auto overloaded = absl::Overload( // - [](int a, int b = 5) { return a * b; }, // - [](int c) { return c; } // - ); + auto overloaded = absl::Overload{ + [](int a, int b = 5) { return a * b; }, + [](int c) { return c; }, + }; + static_assert(!std::is_invocable_v); static_assert(std::is_invocable_v); } TEST(OverloadTest, AmbiguousDuplicatesNotInvocable) { - auto overloaded = absl::Overload( // - [](int a) { return a; }, // - [](int c) { return c; } // - ); + auto overloaded = absl::Overload{ + [](int a) { return a; }, + [](int c) { return c; }, + }; + static_assert(!std::is_invocable_v); } TEST(OverloadTest, AmbiguousConversionNotInvocable) { - auto overloaded = absl::Overload( // - [](uint16_t a) { return a; }, // - [](uint64_t c) { return c; } // - ); + auto overloaded = absl::Overload{ + [](uint16_t a) { return a; }, + [](uint64_t c) { return c; }, + }; + static_assert(!std::is_invocable_v); } TEST(OverloadTest, AmbiguousConversionWithAutoNotInvocable) { - auto overloaded = absl::Overload( // - [](auto a) { return a; }, // - [](auto c) { return c; } // - ); + auto overloaded = absl::Overload{ + [](auto a) { return a; }, + [](auto c) { return c; }, + }; + static_assert(!std::is_invocable_v); } #if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L TEST(OverloadTest, AmbiguousConversionWithAutoAndTemplateNotInvocable) { - auto overloaded = absl::Overload( // - [](auto a) { return a; }, // - [](T c) { return c; } // - ); + auto overloaded = absl::Overload{ + [](auto a) { return a; }, + [](T c) { return c; }, + }; + static_assert(!std::is_invocable_v); } TEST(OverloadTest, DispatchConsidersTypeWithTemplateFallback) { - auto overloaded = absl::Overload( // - [](int a) { return a; }, // - [](T c) { return c * 2; } // - ); + auto overloaded = absl::Overload{ + [](int a) { return a; }, + [](T c) { return c * 2; }, + }; + EXPECT_EQ(7, overloaded(7)); EXPECT_EQ(14.0, overloaded(7.0)); } @@ -133,20 +142,22 @@ TEST(OverloadTest, DispatchConsidersTypeWithTemplateFallback) { #endif // ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L TEST(OverloadTest, DispatchConsidersSfinae) { - auto overloaded = absl::Overload( // - [](auto a) -> decltype(a + 1) { return a + 1; } // - ); + auto overloaded = absl::Overload{ + [](auto a) -> decltype(a + 1) { return a + 1; }, + }; + static_assert(std::is_invocable_v); static_assert(!std::is_invocable_v); } TEST(OverloadTest, VariantVisitDispatchesCorrectly) { absl::variant v(1); - auto overloaded = absl::Overload( - [](int) -> absl::string_view { return "int"; }, // - [](double) -> absl::string_view { return "double"; }, // - [](const std::string&) -> absl::string_view { return "string"; } // - ); + auto overloaded = absl::Overload{ + [](int) -> absl::string_view { return "int"; }, + [](double) -> absl::string_view { return "double"; }, + [](const std::string&) -> absl::string_view { return "string"; }, + }; + EXPECT_EQ("int", absl::visit(overloaded, v)); v = 1.1; EXPECT_EQ("double", absl::visit(overloaded, v)); @@ -156,10 +167,11 @@ TEST(OverloadTest, VariantVisitDispatchesCorrectly) { TEST(OverloadTest, VariantVisitWithAutoFallbackDispatchesCorrectly) { absl::variant v(int32_t{1}); - auto overloaded = absl::Overload( - [](const std::string& s) { return s.size(); }, // - [](const auto& s) { return sizeof(s); } // - ); + auto overloaded = absl::Overload{ + [](const std::string& s) { return s.size(); }, + [](const auto& s) { return sizeof(s); }, + }; + EXPECT_EQ(4, absl::visit(overloaded, v)); v = int64_t{1}; EXPECT_EQ(8, absl::visit(overloaded, v)); @@ -167,6 +179,23 @@ TEST(OverloadTest, VariantVisitWithAutoFallbackDispatchesCorrectly) { EXPECT_EQ(5, absl::visit(overloaded, v)); } +// This API used to be exported as a function, so it should also work fine to +// use parantheses when initializing it. +TEST(OverloadTest, UseWithParentheses) { + const auto overloaded = + absl::Overload([](const std::string& s) { return s.size(); }, + [](const auto& s) { return sizeof(s); }); + + absl::variant v(int32_t{1}); + EXPECT_EQ(4, absl::visit(overloaded, v)); + + v = int64_t{1}; + EXPECT_EQ(8, absl::visit(overloaded, v)); + + v = std::string("hello"); + EXPECT_EQ(5, absl::visit(overloaded, v)); +} + } // namespace #endif -- cgit v1.2.3 From 8bb0b50320274e4d8e0b63d26eafe1871f5241c0 Mon Sep 17 00:00:00 2001 From: Dino Radakovic Date: Thu, 23 May 2024 16:00:58 -0700 Subject: `overload_test`: Remove a few unnecessary trailing return types `absl::StrCat` always returns a `std::string`. Specifying it using a trailing return type does not help. Tests are documentation, and they should be consistent with [the google style guide](https://google.github.io/styleguide/cppguide.html#trailing_return). For example, this is different than `-> absl::string_view` and returning string literals, where it is actually a load-bearing change. PiperOrigin-RevId: 636705683 Change-Id: I0d84a562a59bc0c16be01dd2ae5538adb401432e --- absl/functional/overload_test.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'absl/functional/overload_test.cc') diff --git a/absl/functional/overload_test.cc b/absl/functional/overload_test.cc index a2e87674..92f0eb9b 100644 --- a/absl/functional/overload_test.cc +++ b/absl/functional/overload_test.cc @@ -32,12 +32,10 @@ namespace { TEST(OverloadTest, DispatchConsidersTypeWithAutoFallback) { auto overloaded = absl::Overload{ - [](int v) -> std::string { return absl::StrCat("int ", v); }, - [](double v) -> std::string { return absl::StrCat("double ", v); }, - [](const char* v) -> std::string { - return absl::StrCat("const char* ", v); - }, - [](auto v) -> std::string { return absl::StrCat("auto ", v); }, + [](int v) { return absl::StrCat("int ", v); }, + [](double v) { return absl::StrCat("double ", v); }, + [](const char* v) { return absl::StrCat("const char* ", v); }, + [](auto v) { return absl::StrCat("auto ", v); }, }; EXPECT_EQ("int 1", overloaded(1)); -- cgit v1.2.3 From 49c1f36ec50b3f812f9031d1cf35f1f514eb2a21 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Fri, 24 May 2024 14:10:10 -0700 Subject: overload: make the constructor constexpr PiperOrigin-RevId: 637029703 Change-Id: I58b4c3a3f1aab3062b51d15c5b9fecfd3ca3193a --- absl/functional/overload.h | 2 +- absl/functional/overload_test.cc | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'absl/functional/overload_test.cc') diff --git a/absl/functional/overload.h b/absl/functional/overload.h index 34ffa689..7e19e705 100644 --- a/absl/functional/overload.h +++ b/absl/functional/overload.h @@ -62,7 +62,7 @@ struct Overload final : T... { // aggregate initialization. Before then we must provide a constructor that // makes this work. // - explicit Overload(T... ts) : T(std::move(ts))... {} + constexpr explicit Overload(T... ts) : T(std::move(ts))... {} }; // Before C++20, which added support for CTAD for aggregate types, we must also diff --git a/absl/functional/overload_test.cc b/absl/functional/overload_test.cc index 92f0eb9b..fa49d298 100644 --- a/absl/functional/overload_test.cc +++ b/absl/functional/overload_test.cc @@ -194,6 +194,20 @@ TEST(OverloadTest, UseWithParentheses) { EXPECT_EQ(5, absl::visit(overloaded, v)); } +TEST(OverloadTest, HasConstexprConstructor) { + constexpr auto overloaded = absl::Overload{ + [](int v) { return absl::StrCat("int ", v); }, + [](double v) { return absl::StrCat("double ", v); }, + [](const char* v) { return absl::StrCat("const char* ", v); }, + [](auto v) { return absl::StrCat("auto ", v); }, + }; + + EXPECT_EQ("int 1", overloaded(1)); + EXPECT_EQ("double 2.5", overloaded(2.5)); + EXPECT_EQ("const char* hello", overloaded("hello")); + EXPECT_EQ("auto 1.5", overloaded(1.5f)); +} + } // namespace #endif -- cgit v1.2.3