diff options
Diffstat (limited to 'absl/functional')
-rw-r--r-- | absl/functional/BUILD.bazel | 2 | ||||
-rw-r--r-- | absl/functional/CMakeLists.txt | 3 | ||||
-rw-r--r-- | absl/functional/any_invocable.h | 10 | ||||
-rw-r--r-- | absl/functional/any_invocable_test.cc | 8 | ||||
-rw-r--r-- | absl/functional/bind_front.h | 2 | ||||
-rw-r--r-- | absl/functional/function_ref.h | 6 | ||||
-rw-r--r-- | absl/functional/function_ref_test.cc | 32 | ||||
-rw-r--r-- | absl/functional/internal/any_invocable.h | 61 | ||||
-rw-r--r-- | absl/functional/internal/function_ref.h | 28 |
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&) {} |