Skip to content

Commit

Permalink
absl::Overload() which returns a functor that provides overloads ba…
Browse files Browse the repository at this point in the history
…sed on the functors passed to it.

PiperOrigin-RevId: 568476251
Change-Id: Ic625c9b5300d1db496979c178ca1e655581f9276
  • Loading branch information
Abseil Team authored and copybara-github committed Sep 26, 2023
1 parent d53ca3b commit f4c6246
Show file tree
Hide file tree
Showing 5 changed files with 256 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMake/AbseilDll.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ set(ABSL_INTERNAL_DLL_FILES
"functional/function_ref.h"
"functional/internal/any_invocable.h"
"functional/internal/function_ref.h"
"functional/overload.h"
"hash/hash.h"
"hash/internal/city.h"
"hash/internal/city.cc"
Expand Down
26 changes: 26 additions & 0 deletions absl/functional/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,32 @@ cc_test(
],
)

cc_library(
name = "overload",
hdrs = ["overload.h"],
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
"//absl/base:config",
"//absl/meta:type_traits",
],
)

cc_test(
name = "overload_test",
size = "small",
srcs = ["overload_test.cc"],
copts = ABSL_TEST_COPTS,
deps = [
":overload",
"//absl/base:config",
"//absl/strings",
"//absl/strings:string_view",
"//absl/types:variant",
"@com_google_googletest//:gtest_main",
],
)

cc_test(
name = "function_type_benchmark",
srcs = [
Expand Down
24 changes: 24 additions & 0 deletions absl/functional/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,27 @@ absl_cc_test(
absl::test_instance_tracker
GTest::gmock_main
)

absl_cc_library(
NAME
overload
HDRS
"overload.h"
COPTS
${ABSL_DEFAULT_COPTS}
DEPS
absl::meta
PUBLIC
)

absl_cc_test(
NAME
overload_test
SRCS
"overload_test.cc"
COPTS
${ABSL_TEST_COPTS}
DEPS
absl::strings
GTest::gmock_main
)
75 changes: 75 additions & 0 deletions absl/functional/overload.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2023 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// -----------------------------------------------------------------------------
// File: overload.h
// -----------------------------------------------------------------------------
//
// `absl::Overload()` returns a functor that provides overloads based on the
// functors passed to it.
// 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<int, std::string, double> 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: This requires C++17.

#ifndef ABSL_FUNCTIONAL_OVERLOAD_H_
#define ABSL_FUNCTIONAL_OVERLOAD_H_

#include "absl/base/config.h"
#include "absl/meta/type_traits.h"

namespace absl {
ABSL_NAMESPACE_BEGIN

#if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L

template <int&... ExplicitArgumentBarrier, typename... T>
auto Overload(T&&... ts) {
struct OverloadImpl : absl::remove_cvref_t<T>... {
using absl::remove_cvref_t<T>::operator()...;
};
return OverloadImpl{std::forward<T>(ts)...};
}
#else
namespace functional_internal {
template <typename T>
constexpr bool kDependentFalse = false;
}

template <typename Dependent = int, typename... T>
auto Overload(T&&...) {
static_assert(functional_internal::kDependentFalse<Dependent>,
"Overload is only usable with C++17 or above.");
}

#endif
ABSL_NAMESPACE_END
} // namespace absl

#endif // ABSL_FUNCTIONAL_OVERLOAD_H_
130 changes: 130 additions & 0 deletions absl/functional/overload_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright 2023 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "absl/functional/overload.h"

#include <cstdint>
#include <string>
#include <type_traits>

#include "absl/base/config.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/types/variant.h"

#if defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L

#include "gtest/gtest.h"

namespace {

TEST(OverloadTest, DispatchConsidersType) {
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"));
EXPECT_EQ("auto 1.5", overloaded(1.5f));
}

TEST(OverloadTest, DispatchConsidersNumberOfArguments) {
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"; } //
);
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; } //
);

EXPECT_EQ(21, overloaded(3, 7));
EXPECT_EQ(35, overloaded(7));
EXPECT_EQ(2.5, overloaded(2.5));
}

TEST(OverloadTest, AmbiguousWithDefaultsNotInvocable) {
auto overloaded = absl::Overload( //
[](int a, int b = 5) { return a * b; }, //
[](int c) { return c; } //
);
static_assert(!std::is_invocable_v<decltype(overloaded), int>);
static_assert(std::is_invocable_v<decltype(overloaded), int, int>);
}

TEST(OverloadTest, AmbiguousDuplicatesNotInvocable) {
auto overloaded = absl::Overload( //
[](int a) { return a; }, //
[](int c) { return c; } //
);
static_assert(!std::is_invocable_v<decltype(overloaded), int>);
}

TEST(OverloadTest, AmbiguousConversionNotInvocable) {
auto overloaded = absl::Overload( //
[](uint16_t a) { return a; }, //
[](uint64_t c) { return c; } //
);
static_assert(!std::is_invocable_v<decltype(overloaded), int>);
}

TEST(OverloadTest, DispatchConsidersSfinae) {
auto overloaded = absl::Overload( //
[](auto a) -> decltype(a + 1) { return a + 1; } //
);
static_assert(std::is_invocable_v<decltype(overloaded), int>);
static_assert(!std::is_invocable_v<decltype(overloaded), std::string>);
}

TEST(OverloadTest, VariantVisitDispatchesCorrectly) {
absl::variant<int, double, std::string> 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"; } //
);
EXPECT_EQ("int", absl::visit(overloaded, v));
v = 1.1;
EXPECT_EQ("double", absl::visit(overloaded, v));
v = "hello";
EXPECT_EQ("string", absl::visit(overloaded, v));
}

} // namespace

#endif

0 comments on commit f4c6246

Please sign in to comment.