summaryrefslogtreecommitdiff
path: root/absl/functional
diff options
context:
space:
mode:
Diffstat (limited to 'absl/functional')
-rw-r--r--absl/functional/CMakeLists.txt4
-rw-r--r--absl/functional/any_invocable.h16
-rw-r--r--absl/functional/bind_front.h5
-rw-r--r--absl/functional/internal/any_invocable.h14
-rw-r--r--absl/functional/internal/front_binder.h18
-rw-r--r--absl/functional/overload.h61
-rw-r--r--absl/functional/overload_test.cc169
7 files changed, 201 insertions, 86 deletions
diff --git a/absl/functional/CMakeLists.txt b/absl/functional/CMakeLists.txt
index 602829cb..38fea8b8 100644
--- a/absl/functional/CMakeLists.txt
+++ b/absl/functional/CMakeLists.txt
@@ -129,6 +129,10 @@ absl_cc_test(
COPTS
${ABSL_TEST_COPTS}
DEPS
+ absl::config
+ absl::overload
+ absl::string_view
absl::strings
+ absl::variant
GTest::gmock_main
)
diff --git a/absl/functional/any_invocable.h b/absl/functional/any_invocable.h
index 68d88253..3acb9fd0 100644
--- a/absl/functional/any_invocable.h
+++ b/absl/functional/any_invocable.h
@@ -34,6 +34,7 @@
#define ABSL_FUNCTIONAL_ANY_INVOCABLE_H_
#include <cstddef>
+#include <functional>
#include <initializer_list>
#include <type_traits>
#include <utility>
@@ -98,9 +99,9 @@ ABSL_NAMESPACE_BEGIN
// `AnyInvocable` also properly respects `const` qualifiers, reference
// qualifiers, and the `noexcept` specification (only in C++ 17 and beyond) as
// part of the user-specified function type (e.g.
-// `AnyInvocable<void()&& const noexcept>`). These qualifiers will be applied to
-// the `AnyInvocable` object's `operator()`, and the underlying invocable must
-// be compatible with those qualifiers.
+// `AnyInvocable<void() const && noexcept>`). These qualifiers will be applied
+// to the `AnyInvocable` object's `operator()`, and the underlying invocable
+// must be compatible with those qualifiers.
//
// Comparison of const and non-const function types:
//
@@ -151,6 +152,12 @@ ABSL_NAMESPACE_BEGIN
//
// Attempting to call `absl::AnyInvocable` multiple times in such a case
// results in undefined behavior.
+//
+// Invoking an empty `absl::AnyInvocable` results in undefined behavior:
+//
+// // Create an empty instance using the default constructor.
+// AnyInvocable<void()> empty;
+// empty(); // WARNING: Undefined behavior!
template <class Sig>
class AnyInvocable : private internal_any_invocable::Impl<Sig> {
private:
@@ -167,6 +174,7 @@ class AnyInvocable : private internal_any_invocable::Impl<Sig> {
// Constructors
// Constructs the `AnyInvocable` in an empty state.
+ // Invoking it results in undefined behavior.
AnyInvocable() noexcept = default;
AnyInvocable(std::nullptr_t) noexcept {} // NOLINT
@@ -277,6 +285,8 @@ class AnyInvocable : private internal_any_invocable::Impl<Sig> {
// In other words:
// std::function<void()> f; // empty
// absl::AnyInvocable<void()> a = std::move(f); // not empty
+ //
+ // Invoking an empty `AnyInvocable` results in undefined behavior.
explicit operator bool() const noexcept { return this->HasValue(); }
// Invokes the target object of `*this`. `*this` must not be empty.
diff --git a/absl/functional/bind_front.h b/absl/functional/bind_front.h
index a956eb02..885f24b8 100644
--- a/absl/functional/bind_front.h
+++ b/absl/functional/bind_front.h
@@ -34,6 +34,8 @@
#include <functional> // For std::bind_front.
#endif // defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L
+#include <utility>
+
#include "absl/functional/internal/front_binder.h"
#include "absl/utility/utility.h"
@@ -182,8 +184,7 @@ template <class F, class... BoundArgs>
constexpr functional_internal::bind_front_t<F, BoundArgs...> bind_front(
F&& func, BoundArgs&&... args) {
return functional_internal::bind_front_t<F, BoundArgs...>(
- absl::in_place, absl::forward<F>(func),
- absl::forward<BoundArgs>(args)...);
+ absl::in_place, std::forward<F>(func), std::forward<BoundArgs>(args)...);
}
#endif // defined(__cpp_lib_bind_front) && __cpp_lib_bind_front >= 201907L
diff --git a/absl/functional/internal/any_invocable.h b/absl/functional/internal/any_invocable.h
index b04436d1..c2d8cd47 100644
--- a/absl/functional/internal/any_invocable.h
+++ b/absl/functional/internal/any_invocable.h
@@ -19,11 +19,11 @@
////////////////////////////////////////////////////////////////////////////////
// //
-// This implementation of the proposed `any_invocable` uses an approach that //
-// chooses between local storage and remote storage for the contained target //
-// object based on the target object's size, alignment requirements, and //
-// whether or not it has a nothrow move constructor. Additional optimizations //
-// are performed when the object is a trivially copyable type [basic.types]. //
+// This implementation chooses between local storage and remote storage for //
+// the contained target object based on the target object's size, alignment //
+// requirements, and whether or not it has a nothrow move constructor. //
+// Additional optimizations are performed when the object is a trivially //
+// copyable type [basic.types]. //
// //
// There are three datamembers per `AnyInvocable` instance //
// //
@@ -39,7 +39,7 @@
// target object, directly returning the result. //
// //
// When in the logically empty state, the manager function is an empty //
-// function and the invoker function is one that would be undefined-behavior //
+// function and the invoker function is one that would be undefined behavior //
// to call. //
// //
// An additional optimization is performed when converting from one //
@@ -58,12 +58,12 @@
#include <cstring>
#include <exception>
#include <functional>
-#include <initializer_list>
#include <memory>
#include <new>
#include <type_traits>
#include <utility>
+#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/internal/invoke.h"
#include "absl/base/macros.h"
diff --git a/absl/functional/internal/front_binder.h b/absl/functional/internal/front_binder.h
index 45f52de7..44a54928 100644
--- a/absl/functional/internal/front_binder.h
+++ b/absl/functional/internal/front_binder.h
@@ -34,8 +34,8 @@ namespace functional_internal {
template <class R, class Tuple, size_t... Idx, class... Args>
R Apply(Tuple&& bound, absl::index_sequence<Idx...>, Args&&... free) {
return base_internal::invoke(
- absl::forward<Tuple>(bound).template get<Idx>()...,
- absl::forward<Args>(free)...);
+ std::forward<Tuple>(bound).template get<Idx>()...,
+ std::forward<Args>(free)...);
}
template <class F, class... BoundArgs>
@@ -48,13 +48,13 @@ class FrontBinder {
public:
template <class... Ts>
constexpr explicit FrontBinder(absl::in_place_t, Ts&&... ts)
- : bound_args_(absl::forward<Ts>(ts)...) {}
+ : bound_args_(std::forward<Ts>(ts)...) {}
template <class... FreeArgs, class R = base_internal::invoke_result_t<
F&, BoundArgs&..., FreeArgs&&...>>
R operator()(FreeArgs&&... free_args) & {
return functional_internal::Apply<R>(bound_args_, Idx(),
- absl::forward<FreeArgs>(free_args)...);
+ std::forward<FreeArgs>(free_args)...);
}
template <class... FreeArgs,
@@ -62,7 +62,7 @@ class FrontBinder {
const F&, const BoundArgs&..., FreeArgs&&...>>
R operator()(FreeArgs&&... free_args) const& {
return functional_internal::Apply<R>(bound_args_, Idx(),
- absl::forward<FreeArgs>(free_args)...);
+ std::forward<FreeArgs>(free_args)...);
}
template <class... FreeArgs, class R = base_internal::invoke_result_t<
@@ -70,8 +70,8 @@ class FrontBinder {
R operator()(FreeArgs&&... free_args) && {
// This overload is called when *this is an rvalue. If some of the bound
// arguments are stored by value or rvalue reference, we move them.
- return functional_internal::Apply<R>(absl::move(bound_args_), Idx(),
- absl::forward<FreeArgs>(free_args)...);
+ return functional_internal::Apply<R>(std::move(bound_args_), Idx(),
+ std::forward<FreeArgs>(free_args)...);
}
template <class... FreeArgs,
@@ -80,8 +80,8 @@ class FrontBinder {
R operator()(FreeArgs&&... free_args) const&& {
// This overload is called when *this is an rvalue. If some of the bound
// arguments are stored by value or rvalue reference, we move them.
- return functional_internal::Apply<R>(absl::move(bound_args_), Idx(),
- absl::forward<FreeArgs>(free_args)...);
+ return functional_internal::Apply<R>(std::move(bound_args_), Idx(),
+ std::forward<FreeArgs>(free_args)...);
}
};
diff --git a/absl/functional/overload.h b/absl/functional/overload.h
index 4651f14b..7e19e705 100644
--- a/absl/functional/overload.h
+++ b/absl/functional/overload.h
@@ -16,26 +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<int, std::string, double> v(int{1});
+// Note: absl::Overload requires C++17.
+//
+// Example:
//
-// 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");
+// std::variant<std::string, int32_t, int64_t> 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: This requires C++17.
#ifndef ABSL_FUNCTIONAL_OVERLOAD_H_
#define ABSL_FUNCTIONAL_OVERLOAD_H_
@@ -49,14 +49,30 @@ 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)...};
-}
+template <typename... T>
+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.
+ //
+ constexpr 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 <typename... T>
+Overload(T...) -> Overload<T...>;
+
#else
+
namespace functional_internal {
template <typename T>
constexpr bool kDependentFalse = false;
@@ -69,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 739c4c4c..fa49d298 100644
--- a/absl/functional/overload_test.cc
+++ b/absl/functional/overload_test.cc
@@ -30,15 +30,14 @@
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); } //
- );
+TEST(OverloadTest, DispatchConsidersTypeWithAutoFallback) {
+ 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"));
@@ -46,32 +45,34 @@ TEST(OverloadTest, DispatchConsidersType) {
}
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,45 +80,82 @@ 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<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; } //
- );
+ 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; } //
- );
+ 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, AmbiguousConversionWithAutoNotInvocable) {
+ auto overloaded = absl::Overload{
+ [](auto a) { return a; },
+ [](auto c) { return c; },
+ };
+
static_assert(!std::is_invocable_v<decltype(overloaded), int>);
}
+#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
+
+TEST(OverloadTest, AmbiguousConversionWithAutoAndTemplateNotInvocable) {
+ auto overloaded = absl::Overload{
+ [](auto a) { return a; },
+ []<class T>(T c) { return c; },
+ };
+
+ static_assert(!std::is_invocable_v<decltype(overloaded), int>);
+}
+
+TEST(OverloadTest, DispatchConsidersTypeWithTemplateFallback) {
+ auto overloaded = absl::Overload{
+ [](int a) { return a; },
+ []<class T>(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; } //
- );
+ 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"; } //
- );
+ 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));
@@ -125,6 +163,51 @@ TEST(OverloadTest, VariantVisitDispatchesCorrectly) {
EXPECT_EQ("string", absl::visit(overloaded, v));
}
+TEST(OverloadTest, VariantVisitWithAutoFallbackDispatchesCorrectly) {
+ absl::variant<std::string, int32_t, int64_t> 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));
+}
+
+// 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<std::string, int32_t, int64_t> 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));
+}
+
+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