summaryrefslogtreecommitdiff
path: root/absl/functional
diff options
context:
space:
mode:
Diffstat (limited to 'absl/functional')
-rw-r--r--absl/functional/BUILD.bazel2
-rw-r--r--absl/functional/CMakeLists.txt3
-rw-r--r--absl/functional/any_invocable.h10
-rw-r--r--absl/functional/any_invocable_test.cc8
-rw-r--r--absl/functional/bind_front.h2
-rw-r--r--absl/functional/function_ref.h6
-rw-r--r--absl/functional/function_ref_test.cc32
-rw-r--r--absl/functional/internal/any_invocable.h61
-rw-r--r--absl/functional/internal/function_ref.h28
9 files changed, 108 insertions, 44 deletions
diff --git a/absl/functional/BUILD.bazel b/absl/functional/BUILD.bazel
index c4fbce98..4ceac539 100644
--- a/absl/functional/BUILD.bazel
+++ b/absl/functional/BUILD.bazel
@@ -92,6 +92,7 @@ cc_library(
copts = ABSL_DEFAULT_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
+ ":any_invocable",
"//absl/base:base_internal",
"//absl/base:core_headers",
"//absl/meta:type_traits",
@@ -104,6 +105,7 @@ cc_test(
srcs = ["function_ref_test.cc"],
copts = ABSL_TEST_COPTS,
deps = [
+ ":any_invocable",
":function_ref",
"//absl/container:test_instance_tracker",
"//absl/memory",
diff --git a/absl/functional/CMakeLists.txt b/absl/functional/CMakeLists.txt
index c0f6eaaa..c704e049 100644
--- a/absl/functional/CMakeLists.txt
+++ b/absl/functional/CMakeLists.txt
@@ -39,7 +39,7 @@ absl_cc_test(
"any_invocable_test.cc"
"internal/any_invocable.h"
COPTS
- ${ABSL_DEFAULT_COPTS}
+ ${ABSL_TEST_COPTS}
DEPS
absl::any_invocable
absl::base_internal
@@ -90,6 +90,7 @@ absl_cc_library(
DEPS
absl::base_internal
absl::core_headers
+ absl::any_invocable
absl::meta
PUBLIC
)
diff --git a/absl/functional/any_invocable.h b/absl/functional/any_invocable.h
index 3e783c87..68d88253 100644
--- a/absl/functional/any_invocable.h
+++ b/absl/functional/any_invocable.h
@@ -266,9 +266,17 @@ class AnyInvocable : private internal_any_invocable::Impl<Sig> {
// Exchanges the targets of `*this` and `other`.
void swap(AnyInvocable& other) noexcept { std::swap(*this, other); }
- // abl::AnyInvocable::operator bool()
+ // absl::AnyInvocable::operator bool()
//
// Returns `true` if `*this` is not empty.
+ //
+ // WARNING: An `AnyInvocable` that wraps an empty `std::function` is not
+ // itself empty. This behavior is consistent with the standard equivalent
+ // `std::move_only_function`.
+ //
+ // In other words:
+ // std::function<void()> f; // empty
+ // absl::AnyInvocable<void()> a = std::move(f); // not empty
explicit operator bool() const noexcept { return this->HasValue(); }
// Invokes the target object of `*this`. `*this` must not be empty.
diff --git a/absl/functional/any_invocable_test.cc b/absl/functional/any_invocable_test.cc
index 1ed85407..a740faa6 100644
--- a/absl/functional/any_invocable_test.cc
+++ b/absl/functional/any_invocable_test.cc
@@ -1418,7 +1418,7 @@ TYPED_TEST_P(AnyInvTestRvalue, NonConstCrashesOnSecondCall) {
// Ensure we're still valid
EXPECT_TRUE(static_cast<bool>(fun)); // NOLINT(bugprone-use-after-move)
-#if !defined(NDEBUG) || ABSL_OPTION_HARDENED == 1
+#if !defined(NDEBUG)
EXPECT_DEATH_IF_SUPPORTED(std::move(fun)(7, 8, 9), "");
#endif
}
@@ -1431,14 +1431,14 @@ TYPED_TEST_P(AnyInvTestRvalue, QualifierIndependentObjectLifetime) {
auto refs = std::make_shared<std::nullptr_t>();
{
AnyInvType fun([refs](auto&&...) noexcept { return 0; });
- EXPECT_FALSE(refs.unique());
+ EXPECT_GT(refs.use_count(), 1);
std::move(fun)(7, 8, 9);
// Ensure destructor hasn't run even if rref-qualified
- EXPECT_FALSE(refs.unique());
+ EXPECT_GT(refs.use_count(), 1);
}
- EXPECT_TRUE(refs.unique());
+ EXPECT_EQ(refs.use_count(), 1);
}
// NOTE: This test suite originally attempted to enumerate all possible
diff --git a/absl/functional/bind_front.h b/absl/functional/bind_front.h
index f9075bd1..a956eb02 100644
--- a/absl/functional/bind_front.h
+++ b/absl/functional/bind_front.h
@@ -46,7 +46,7 @@ ABSL_NAMESPACE_BEGIN
//
// Like `std::bind()`, `absl::bind_front()` is implicitly convertible to
// `std::function`. In particular, it may be used as a simpler replacement for
-// `std::bind()` in most cases, as it does not require placeholders to be
+// `std::bind()` in most cases, as it does not require placeholders to be
// specified. More importantly, it provides more reliable correctness guarantees
// than `std::bind()`; while `std::bind()` will silently ignore passing more
// parameters than expected, for example, `absl::bind_front()` will report such
diff --git a/absl/functional/function_ref.h b/absl/functional/function_ref.h
index f9779607..2b9139d3 100644
--- a/absl/functional/function_ref.h
+++ b/absl/functional/function_ref.h
@@ -66,11 +66,11 @@ class FunctionRef;
// FunctionRef
//
-// An `absl::FunctionRef` is a lightweight wrapper to any invokable object with
+// An `absl::FunctionRef` is a lightweight wrapper to any invocable object with
// a compatible signature. Generally, an `absl::FunctionRef` should only be used
// as an argument type and should be preferred as an argument over a const
// reference to a `std::function`. `absl::FunctionRef` itself does not allocate,
-// although the wrapped invokable may.
+// although the wrapped invocable may.
//
// Example:
//
@@ -98,7 +98,7 @@ class FunctionRef<R(Args...)> {
std::is_convertible<FR, R>::value>::type;
public:
- // Constructs a FunctionRef from any invokable type.
+ // Constructs a FunctionRef from any invocable type.
template <typename F, typename = EnableIfCompatible<const F&>>
// NOLINTNEXTLINE(runtime/explicit)
FunctionRef(const F& f ABSL_ATTRIBUTE_LIFETIME_BOUND)
diff --git a/absl/functional/function_ref_test.cc b/absl/functional/function_ref_test.cc
index 412027cd..c61117eb 100644
--- a/absl/functional/function_ref_test.cc
+++ b/absl/functional/function_ref_test.cc
@@ -20,6 +20,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/container/internal/test_instance_tracker.h"
+#include "absl/functional/any_invocable.h"
#include "absl/memory/memory.h"
namespace absl {
@@ -157,6 +158,25 @@ TEST(FunctionRef, NullMemberPtrAssertFails) {
EXPECT_DEBUG_DEATH({ FunctionRef<int(const S& s)> ref(mem_ptr); }, "");
}
+TEST(FunctionRef, NullStdFunctionAssertPasses) {
+ std::function<void()> function = []() {};
+ FunctionRef<void()> ref(function);
+}
+
+TEST(FunctionRef, NullStdFunctionAssertFails) {
+ std::function<void()> function = nullptr;
+ EXPECT_DEBUG_DEATH({ FunctionRef<void()> ref(function); }, "");
+}
+
+TEST(FunctionRef, NullAnyInvocableAssertPasses) {
+ AnyInvocable<void() const> invocable = []() {};
+ FunctionRef<void()> ref(invocable);
+}
+TEST(FunctionRef, NullAnyInvocableAssertFails) {
+ AnyInvocable<void() const> invocable = nullptr;
+ EXPECT_DEBUG_DEATH({ FunctionRef<void()> ref(invocable); }, "");
+}
+
#endif // GTEST_HAS_DEATH_TEST
TEST(FunctionRef, CopiesAndMovesPerPassByValue) {
@@ -237,7 +257,7 @@ TEST(FunctionRef, PassByValueTypes) {
"Reference types should be preserved");
// Make sure the address of an object received by reference is the same as the
- // addess of the object passed by the caller.
+ // address of the object passed by the caller.
{
LargeTrivial obj;
auto test = [&obj](LargeTrivial& input) { ASSERT_EQ(&input, &obj); };
@@ -253,6 +273,16 @@ TEST(FunctionRef, PassByValueTypes) {
}
}
+TEST(FunctionRef, ReferenceToIncompleteType) {
+ struct IncompleteType;
+ auto test = [](IncompleteType&) {};
+ absl::FunctionRef<void(IncompleteType&)> ref(test);
+
+ struct IncompleteType {};
+ IncompleteType obj;
+ ref(obj);
+}
+
} // namespace
ABSL_NAMESPACE_END
} // namespace absl
diff --git a/absl/functional/internal/any_invocable.h b/absl/functional/internal/any_invocable.h
index 6bfbda18..f096bb02 100644
--- a/absl/functional/internal/any_invocable.h
+++ b/absl/functional/internal/any_invocable.h
@@ -56,6 +56,7 @@
#include <cassert>
#include <cstddef>
#include <cstring>
+#include <exception>
#include <functional>
#include <initializer_list>
#include <memory>
@@ -134,8 +135,16 @@ void InvokeR(F&& f, P&&... args) {
template <class ReturnType, class F, class... P,
absl::enable_if_t<!std::is_void<ReturnType>::value, int> = 0>
ReturnType InvokeR(F&& f, P&&... args) {
+ // GCC 12 has a false-positive -Wmaybe-uninitialized warning here.
+#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
return absl::base_internal::invoke(std::forward<F>(f),
std::forward<P>(args)...);
+#if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0)
+#pragma GCC diagnostic pop
+#endif
}
//
@@ -196,7 +205,7 @@ union TypeErasedState {
template <class T>
T& ObjectInLocalStorage(TypeErasedState* const state) {
// We launder here because the storage may be reused with the same type.
-#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
+#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L
return *std::launder(reinterpret_cast<T*>(&state->storage));
#elif ABSL_HAVE_BUILTIN(__builtin_launder)
return *__builtin_launder(reinterpret_cast<T*>(&state->storage));
@@ -431,11 +440,11 @@ class CoreImpl {
CoreImpl() noexcept : manager_(EmptyManager), invoker_(nullptr) {}
- enum class TargetType : int {
- kPointer = 0,
- kCompatibleAnyInvocable = 1,
- kIncompatibleAnyInvocable = 2,
- kOther = 3,
+ enum class TargetType {
+ kPointer,
+ kCompatibleAnyInvocable,
+ kIncompatibleAnyInvocable,
+ kOther,
};
// Note: QualDecayedTRef here includes the cv-ref qualifiers associated with
@@ -457,8 +466,7 @@ class CoreImpl {
// NOTE: We only use integers instead of enums as template parameters in
// order to work around a bug on C++14 under MSVC 2017.
// See b/236131881.
- Initialize<static_cast<int>(kTargetType), QualDecayedTRef>(
- std::forward<F>(f));
+ Initialize<kTargetType, QualDecayedTRef>(std::forward<F>(f));
}
// Note: QualTRef here includes the cv-ref qualifiers associated with the
@@ -487,7 +495,7 @@ class CoreImpl {
// object.
Clear();
- // Perform the actual move/destory operation on the target function.
+ // Perform the actual move/destroy operation on the target function.
other.manager_(FunctionToCall::relocate_from_to, &other.state_, &state_);
manager_ = other.manager_;
invoker_ = other.invoker_;
@@ -509,8 +517,8 @@ class CoreImpl {
invoker_ = nullptr;
}
- template <int target_type, class QualDecayedTRef, class F,
- absl::enable_if_t<target_type == 0, int> = 0>
+ template <TargetType target_type, class QualDecayedTRef, class F,
+ absl::enable_if_t<target_type == TargetType::kPointer, int> = 0>
void Initialize(F&& f) {
// This condition handles types that decay into pointers, which includes
// function references. Since function references cannot be null, GCC warns
@@ -534,8 +542,9 @@ class CoreImpl {
InitializeStorage<QualDecayedTRef>(std::forward<F>(f));
}
- template <int target_type, class QualDecayedTRef, class F,
- absl::enable_if_t<target_type == 1, int> = 0>
+ template <TargetType target_type, class QualDecayedTRef, class F,
+ absl::enable_if_t<
+ target_type == TargetType::kCompatibleAnyInvocable, int> = 0>
void Initialize(F&& f) {
// In this case we can "steal the guts" of the other AnyInvocable.
f.manager_(FunctionToCall::relocate_from_to, &f.state_, &state_);
@@ -546,8 +555,9 @@ class CoreImpl {
f.invoker_ = nullptr;
}
- template <int target_type, class QualDecayedTRef, class F,
- absl::enable_if_t<target_type == 2, int> = 0>
+ template <TargetType target_type, class QualDecayedTRef, class F,
+ absl::enable_if_t<
+ target_type == TargetType::kIncompatibleAnyInvocable, int> = 0>
void Initialize(F&& f) {
if (f.HasValue()) {
InitializeStorage<QualDecayedTRef>(std::forward<F>(f));
@@ -557,8 +567,8 @@ class CoreImpl {
}
}
- template <int target_type, class QualDecayedTRef, class F,
- typename = absl::enable_if_t<target_type == 3>>
+ template <TargetType target_type, class QualDecayedTRef, class F,
+ typename = absl::enable_if_t<target_type == TargetType::kOther>>
void Initialize(F&& f) {
InitializeStorage<QualDecayedTRef>(std::forward<F>(f));
}
@@ -810,19 +820,22 @@ using CanAssignReferenceWrapper = TrueAlias<
: Core(absl::in_place_type<absl::decay_t<T> inv_quals>, \
std::forward<Args>(args)...) {} \
\
+ /*Raises a fatal error when the AnyInvocable is invoked after a move*/ \
+ static ReturnType InvokedAfterMove( \
+ TypeErasedState*, \
+ ForwardedParameterType<P>...) noexcept(noex) { \
+ ABSL_HARDENING_ASSERT(false && "AnyInvocable use-after-move"); \
+ std::terminate(); \
+ } \
+ \
InvokerType<noex, ReturnType, P...>* ExtractInvoker() cv { \
using QualifiedTestType = int cv ref; \
auto* invoker = this->invoker_; \
if (!std::is_const<QualifiedTestType>::value && \
std::is_rvalue_reference<QualifiedTestType>::value) { \
- ABSL_HARDENING_ASSERT([this]() { \
+ ABSL_ASSERT([this]() { \
/* We checked that this isn't const above, so const_cast is safe */ \
- const_cast<Impl*>(this)->invoker_ = \
- [](TypeErasedState*, \
- ForwardedParameterType<P>...) noexcept(noex) -> ReturnType { \
- ABSL_HARDENING_ASSERT(false && "AnyInvocable use-after-move"); \
- std::terminate(); \
- }; \
+ const_cast<Impl*>(this)->invoker_ = InvokedAfterMove; \
return this->HasValue(); \
}()); \
} \
diff --git a/absl/functional/internal/function_ref.h b/absl/functional/internal/function_ref.h
index b5bb8b43..1cd34a3c 100644
--- a/absl/functional/internal/function_ref.h
+++ b/absl/functional/internal/function_ref.h
@@ -20,6 +20,7 @@
#include <type_traits>
#include "absl/base/internal/invoke.h"
+#include "absl/functional/any_invocable.h"
#include "absl/meta/type_traits.h"
namespace absl {
@@ -40,18 +41,21 @@ union VoidPtr {
// Chooses the best type for passing T as an argument.
// Attempt to be close to SystemV AMD64 ABI. Objects with trivial copy ctor are
// passed by value.
+template <typename T,
+ bool IsLValueReference = std::is_lvalue_reference<T>::value>
+struct PassByValue : std::false_type {};
+
template <typename T>
-constexpr bool PassByValue() {
- return !std::is_lvalue_reference<T>::value &&
- absl::is_trivially_copy_constructible<T>::value &&
- absl::is_trivially_copy_assignable<
- typename std::remove_cv<T>::type>::value &&
- std::is_trivially_destructible<T>::value &&
- sizeof(T) <= 2 * sizeof(void*);
-}
+struct PassByValue<T, /*IsLValueReference=*/false>
+ : std::integral_constant<bool,
+ absl::is_trivially_copy_constructible<T>::value &&
+ absl::is_trivially_copy_assignable<
+ typename std::remove_cv<T>::type>::value &&
+ std::is_trivially_destructible<T>::value &&
+ sizeof(T) <= 2 * sizeof(void*)> {};
template <typename T>
-struct ForwardT : std::conditional<PassByValue<T>(), T, T&&> {};
+struct ForwardT : std::conditional<PassByValue<T>::value, T, T&&> {};
// An Invoker takes a pointer to the type-erased invokable object, followed by
// the arguments that the invokable object expects.
@@ -87,6 +91,12 @@ void AssertNonNull(const std::function<Sig>& f) {
(void)f;
}
+template <typename Sig>
+void AssertNonNull(const AnyInvocable<Sig>& f) {
+ assert(f != nullptr);
+ (void)f;
+}
+
template <typename F>
void AssertNonNull(const F&) {}