| // Copyright 2022 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. | |
| // To prevent compiler memory exhaustion (OOM / Killed signal terminates | |
| // cc1plus) during parallel builds with GCC, the test suite instantiations have | |
| // been split into four separate compilation units: any_invocable_test_inst1.cc, | |
| // any_invocable_test_inst2.cc, any_invocable_test_inst3.cc, and | |
| // any_invocable_test_inst4.cc. The test definitions remain here in this | |
| // header. | |
| // SKIP_ABSL_INLINE_NAMESPACE_CHECK | |
| #ifndef ABSL_FUNCTIONAL_ANY_INVOCABLE_TEST_H_ | |
| #define ABSL_FUNCTIONAL_ANY_INVOCABLE_TEST_H_ | |
| #include <cstddef> | |
| #include <cstdlib> | |
| #include <functional> | |
| #include <initializer_list> | |
| #include <iterator> | |
| #include <memory> | |
| #include <numeric> | |
| #include <type_traits> | |
| #include <utility> | |
| #include "gtest/gtest.h" | |
| #include "absl/base/config.h" | |
| #include "absl/base/nullability.h" | |
| #include "absl/functional/any_invocable.h" | |
| #include "absl/meta/type_traits.h" | |
| #include "absl/utility/utility.h" | |
| static_assert(absl::internal_any_invocable::kStorageSize >= sizeof(void*), | |
| "These tests assume that the small object storage is at least " | |
| "the size of a pointer."); | |
| namespace absl_any_invocable_test { | |
| // A dummy type we use when passing qualifiers to metafunctions | |
| struct _ {}; | |
| template <class T> | |
| struct Wrapper { | |
| template <class U, class = std::enable_if_t<std::is_convertible_v<U, T>>> | |
| Wrapper(U&&); // NOLINT | |
| }; | |
| // This will cause a recursive trait instantiation if the SFINAE checks are | |
| // not ordered correctly for constructibility. | |
| static_assert(std::is_constructible_v<Wrapper<absl::AnyInvocable<void()>>, | |
| Wrapper<absl::AnyInvocable<void()>>>); | |
| // A metafunction that takes the cv and l-value reference qualifiers that were | |
| // associated with a function type (here passed via qualifiers of an object | |
| // type), and . | |
| template <class Qualifiers, class This> | |
| struct QualifiersForThisImpl { | |
| static_assert(std::is_object_v<This>); | |
| using type = | |
| std::conditional_t<std::is_const_v<Qualifiers>, const This, This>&; | |
| }; | |
| template <class Qualifiers, class This> | |
| struct QualifiersForThisImpl<Qualifiers&, This> | |
| : QualifiersForThisImpl<Qualifiers, This> {}; | |
| template <class Qualifiers, class This> | |
| struct QualifiersForThisImpl<Qualifiers&&, This> { | |
| static_assert(std::is_object_v<This>); | |
| using type = | |
| std::conditional_t<std::is_const_v<Qualifiers>, const This, This>&&; | |
| }; | |
| template <class Qualifiers, class This> | |
| using QualifiersForThis = | |
| typename QualifiersForThisImpl<Qualifiers, This>::type; | |
| // A metafunction that takes the cv and l-value reference qualifier of T and | |
| // applies them to U's function type qualifiers. | |
| template <class T, class Fun> | |
| struct GiveQualifiersToFunImpl; | |
| template <class T, class R, class... P> | |
| struct GiveQualifiersToFunImpl<T, R(P...)> { | |
| using type = std::conditional_t<std::is_const_v<T>, R(P...) const, R(P...)>; | |
| }; | |
| template <class T, class R, class... P> | |
| struct GiveQualifiersToFunImpl<T&, R(P...)> { | |
| using type = | |
| std::conditional_t<std::is_const_v<T>, R(P...) const&, R(P...) &>; | |
| }; | |
| template <class T, class R, class... P> | |
| struct GiveQualifiersToFunImpl<T&&, R(P...)> { | |
| using type = | |
| std::conditional_t<std::is_const_v<T>, R(P...) const&&, R(P...) &&>; | |
| }; | |
| template <class T, class R, class... P> | |
| struct GiveQualifiersToFunImpl<T, R(P...) noexcept> { | |
| using type = std::conditional_t<std::is_const_v<T>, R(P...) const noexcept, | |
| R(P...) noexcept>; | |
| }; | |
| template <class T, class R, class... P> | |
| struct GiveQualifiersToFunImpl<T&, R(P...) noexcept> { | |
| using type = std::conditional_t<std::is_const_v<T>, R(P...) const & noexcept, | |
| R(P...) & noexcept>; | |
| }; | |
| template <class T, class R, class... P> | |
| struct GiveQualifiersToFunImpl<T&&, R(P...) noexcept> { | |
| using type = std::conditional_t<std::is_const_v<T>, R(P...) const && noexcept, | |
| R(P...) && noexcept>; | |
| }; | |
| template <class T, class Fun> | |
| using GiveQualifiersToFun = typename GiveQualifiersToFunImpl<T, Fun>::type; | |
| // This is used in template parameters to decide whether or not to use a type | |
| // that fits in the small object optimization storage. | |
| enum class ObjSize { small, large }; | |
| // A base type that is used with classes as a means to insert an | |
| // appropriately-sized dummy datamember when Size is ObjSize::large so that the | |
| // user's class type is guaranteed to not fit in small object storage. | |
| template <ObjSize Size> | |
| struct TypeErasedPadding; | |
| template <> | |
| struct TypeErasedPadding<ObjSize::small> {}; | |
| template <> | |
| struct TypeErasedPadding<ObjSize::large> { | |
| char dummy_data[absl::internal_any_invocable::kStorageSize + 1] = {}; | |
| }; | |
| struct Int { | |
| Int(int v) noexcept : value(v) {} // NOLINT | |
| #ifndef _MSC_VER | |
| Int(Int&&) noexcept { | |
| // NOTE: Prior to C++17, this not being called requires optimizations to | |
| // take place when performing the top-level invocation. In practice, | |
| // most supported compilers perform this optimization prior to C++17. | |
| std::abort(); | |
| } | |
| #else | |
| Int(Int&& v) noexcept = default; | |
| #endif | |
| operator int() && noexcept { return value; } // NOLINT | |
| int MemberFunctionAdd(int const& b, int c) noexcept { // NOLINT | |
| return value + b + c; | |
| } | |
| int value; | |
| }; | |
| enum class Movable { no, yes, nothrow, trivial }; | |
| enum class NothrowCall { no, yes }; | |
| enum class Destructible { nothrow, trivial }; | |
| enum class ObjAlign : std::size_t { | |
| normal = absl::internal_any_invocable::kAlignment, | |
| large = absl::internal_any_invocable::kAlignment * 2, | |
| }; | |
| // A function-object template that has knobs for each property that can affect | |
| // how the object is stored in AnyInvocable. | |
| template <Movable Movability, Destructible Destructibility, class Qual, | |
| NothrowCall CallExceptionSpec, ObjSize Size, ObjAlign Alignment> | |
| struct add; | |
| #define ABSL_INTERNALS_ADD(qual) \ | |
| template <NothrowCall CallExceptionSpec, ObjSize Size, ObjAlign Alignment> \ | |
| struct alignas(static_cast<std::size_t>(Alignment)) \ | |
| add<Movable::trivial, Destructible::trivial, _ qual, CallExceptionSpec, \ | |
| Size, Alignment> : TypeErasedPadding<Size> { \ | |
| explicit add(int state_init) : state(state_init) {} \ | |
| explicit add(std::initializer_list<int> state_init, int tail) \ | |
| : state(std::accumulate(std::begin(state_init), std::end(state_init), \ | |
| 0) + \ | |
| tail) {} \ | |
| add(add&& other) = default; /*NOLINT*/ \ | |
| Int operator()(int a, int b, int c) qual \ | |
| noexcept(CallExceptionSpec == NothrowCall::yes) { \ | |
| return state + a + b + c; \ | |
| } \ | |
| int state; \ | |
| }; \ | |
| \ | |
| template <NothrowCall CallExceptionSpec, ObjSize Size, ObjAlign Alignment> \ | |
| struct alignas(static_cast<std::size_t>(Alignment)) \ | |
| add<Movable::trivial, Destructible::nothrow, _ qual, CallExceptionSpec, \ | |
| Size, Alignment> : TypeErasedPadding<Size> { \ | |
| explicit add(int state_init) : state(state_init) {} \ | |
| explicit add(std::initializer_list<int> state_init, int tail) \ | |
| : state(std::accumulate(std::begin(state_init), std::end(state_init), \ | |
| 0) + \ | |
| tail) {} \ | |
| ~add() noexcept {} \ | |
| add(add&& other) = default; /*NOLINT*/ \ | |
| Int operator()(int a, int b, int c) qual \ | |
| noexcept(CallExceptionSpec == NothrowCall::yes) { \ | |
| return state + a + b + c; \ | |
| } \ | |
| int state; \ | |
| } | |
| // Explicitly specify an empty argument. | |
| // MSVC (at least up to _MSC_VER 1931, if not beyond) warns that | |
| // ABSL_INTERNALS_ADD() is an undefined zero-arg overload. | |
| #define ABSL_INTERNALS_NOARG | |
| ABSL_INTERNALS_ADD(ABSL_INTERNALS_NOARG); | |
| #undef ABSL_INTERNALS_NOARG | |
| ABSL_INTERNALS_ADD(const); | |
| ABSL_INTERNALS_ADD(&); | |
| ABSL_INTERNALS_ADD(const&); | |
| ABSL_INTERNALS_ADD(&&); // NOLINT | |
| ABSL_INTERNALS_ADD(const&&); // NOLINT | |
| #undef ABSL_INTERNALS_ADD | |
| template <Destructible Destructibility, class Qual, | |
| NothrowCall CallExceptionSpec, ObjSize Size, ObjAlign Alignment> | |
| struct add<Movable::no, Destructibility, Qual, CallExceptionSpec, Size, | |
| Alignment> : private add<Movable::trivial, Destructibility, Qual, | |
| CallExceptionSpec, Size, Alignment> { | |
| using Base = add<Movable::trivial, Destructibility, Qual, CallExceptionSpec, | |
| Size, Alignment>; | |
| explicit add(int state_init) : Base(state_init) {} | |
| explicit add(std::initializer_list<int> state_init, int tail) | |
| : Base(state_init, tail) {} | |
| add(add&&) = delete; | |
| using Base::operator(); | |
| using Base::state; | |
| }; | |
| template <Destructible Destructibility, class Qual, | |
| NothrowCall CallExceptionSpec, ObjSize Size, ObjAlign Alignment> | |
| struct add<Movable::yes, Destructibility, Qual, CallExceptionSpec, Size, | |
| Alignment> : private add<Movable::trivial, Destructibility, Qual, | |
| CallExceptionSpec, Size, Alignment> { | |
| using Base = add<Movable::trivial, Destructibility, Qual, CallExceptionSpec, | |
| Size, Alignment>; | |
| explicit add(int state_init) : Base(state_init) {} | |
| explicit add(std::initializer_list<int> state_init, int tail) | |
| : Base(state_init, tail) {} | |
| add(add&& other) noexcept(false) : Base(other.state) {} // NOLINT | |
| using Base::operator(); | |
| using Base::state; | |
| }; | |
| template <Destructible Destructibility, class Qual, | |
| NothrowCall CallExceptionSpec, ObjSize Size, ObjAlign Alignment> | |
| struct add<Movable::nothrow, Destructibility, Qual, CallExceptionSpec, Size, | |
| Alignment> : private add<Movable::trivial, Destructibility, Qual, | |
| CallExceptionSpec, Size, Alignment> { | |
| using Base = add<Movable::trivial, Destructibility, Qual, CallExceptionSpec, | |
| Size, Alignment>; | |
| explicit add(int state_init) : Base(state_init) {} | |
| explicit add(std::initializer_list<int> state_init, int tail) | |
| : Base(state_init, tail) {} | |
| add(add&& other) noexcept : Base(other.state) {} | |
| using Base::operator(); | |
| using Base::state; | |
| }; | |
| // Actual non-member functions rather than function objects | |
| inline Int add_function(Int&& a, int b, int c) noexcept { | |
| return a.value + b + c; | |
| } | |
| inline Int mult_function(Int&& a, int b, int c) noexcept { | |
| return a.value * b * c; | |
| } | |
| inline Int square_function(Int const&& a) noexcept { return a.value * a.value; } | |
| template <class Sig> | |
| using AnyInvocable = absl::AnyInvocable<Sig>; | |
| // Instantiations of this template contains all of the compile-time parameters | |
| // for a given instantiation of the AnyInvocable test suite. | |
| template <Movable Movability, Destructible Destructibility, class Qual, | |
| NothrowCall CallExceptionSpec, ObjSize Size, ObjAlign Alignment> | |
| struct TestParams { | |
| static constexpr Movable kMovability = Movability; | |
| static constexpr Destructible kDestructibility = Destructibility; | |
| using Qualifiers = Qual; | |
| static constexpr NothrowCall kCallExceptionSpec = CallExceptionSpec; | |
| static constexpr bool kIsNoexcept = kCallExceptionSpec == NothrowCall::yes; | |
| static constexpr bool kIsRvalueQualified = std::is_rvalue_reference_v<Qual>; | |
| static constexpr ObjSize kSize = Size; | |
| static constexpr ObjAlign kAlignment = Alignment; | |
| // These types are used when testing with member object pointer Invocables | |
| using UnqualifiedUnaryFunType = int(Int const&&) noexcept(CallExceptionSpec == | |
| NothrowCall::yes); | |
| using UnaryFunType = GiveQualifiersToFun<Qualifiers, UnqualifiedUnaryFunType>; | |
| using MemObjPtrType = int(Int::*); | |
| using UnaryAnyInvType = AnyInvocable<UnaryFunType>; | |
| using UnaryThisParamType = QualifiersForThis<Qualifiers, UnaryAnyInvType>; | |
| template <class T> | |
| static UnaryThisParamType ToUnaryThisParam(T&& fun) { | |
| return static_cast<UnaryThisParamType>(fun); | |
| } | |
| // This function type intentionally uses 3 "kinds" of parameter types. | |
| // - A user-defined type | |
| // - A reference type | |
| // - A scalar type | |
| // | |
| // These were chosen because internal forwarding takes place on parameters | |
| // differently depending based on type properties (scalars are forwarded by | |
| // value). | |
| using ResultType = Int; | |
| using AnyInvocableFunTypeNotNoexcept = Int(Int, const int&, int); | |
| using UnqualifiedFunType = | |
| std::conditional_t<kIsNoexcept, Int(Int, const int&, int) noexcept, | |
| Int(Int, const int&, int)>; | |
| using FunType = GiveQualifiersToFun<Qualifiers, UnqualifiedFunType>; | |
| using MemFunPtrType = | |
| std::conditional_t<kIsNoexcept, Int (Int::*)(const int&, int) noexcept, | |
| Int (Int::*)(const int&, int)>; | |
| using AnyInvType = AnyInvocable<FunType>; | |
| using AddType = add<kMovability, kDestructibility, Qualifiers, | |
| kCallExceptionSpec, kSize, kAlignment>; | |
| using ThisParamType = QualifiersForThis<Qualifiers, AnyInvType>; | |
| template <class T> | |
| static ThisParamType ToThisParam(T&& fun) { | |
| return static_cast<ThisParamType>(fun); | |
| } | |
| // These typedefs are used when testing void return type covariance. | |
| using UnqualifiedVoidFunType = | |
| std::conditional_t<kIsNoexcept, void(Int, const int&, int) noexcept, | |
| void(Int, const int&, int)>; | |
| using VoidFunType = GiveQualifiersToFun<Qualifiers, UnqualifiedVoidFunType>; | |
| using VoidAnyInvType = AnyInvocable<VoidFunType>; | |
| using VoidThisParamType = QualifiersForThis<Qualifiers, VoidAnyInvType>; | |
| template <class T> | |
| static VoidThisParamType ToVoidThisParam(T&& fun) { | |
| return static_cast<VoidThisParamType>(fun); | |
| } | |
| using CompatibleAnyInvocableFunType = | |
| std::conditional_t<std::is_rvalue_reference_v<Qual>, | |
| GiveQualifiersToFun<const _&&, UnqualifiedFunType>, | |
| GiveQualifiersToFun<const _&, UnqualifiedFunType>>; | |
| using CompatibleAnyInvType = AnyInvocable<CompatibleAnyInvocableFunType>; | |
| using IncompatibleInvocable = | |
| std::conditional_t<std::is_rvalue_reference_v<Qual>, | |
| GiveQualifiersToFun<_&, UnqualifiedFunType>(_::*), | |
| GiveQualifiersToFun<_&&, UnqualifiedFunType>(_::*)>; | |
| }; | |
| // Given a member-pointer type, this metafunction yields the target type of the | |
| // pointer, not including the class-type. It is used to verify that the function | |
| // call operator of AnyInvocable has the proper signature, corresponding to the | |
| // function type that the user provided. | |
| template <class MemberPtrType> | |
| struct MemberTypeOfImpl; | |
| template <class Class, class T> | |
| struct MemberTypeOfImpl<T(Class::*)> { | |
| using type = T; | |
| }; | |
| template <class MemberPtrType> | |
| using MemberTypeOf = typename MemberTypeOfImpl<MemberPtrType>::type; | |
| template <class T, class = void> | |
| struct IsMemberSwappableImpl : std::false_type { | |
| static constexpr bool kIsNothrow = false; | |
| }; | |
| template <class T> | |
| struct IsMemberSwappableImpl< | |
| T, std::void_t<decltype(std::declval<T&>().swap(std::declval<T&>()))>> | |
| : std::true_type { | |
| static constexpr bool kIsNothrow = | |
| noexcept(std::declval<T&>().swap(std::declval<T&>())); | |
| }; | |
| template <class T> | |
| using IsMemberSwappable = IsMemberSwappableImpl<T>; | |
| template <class T> | |
| using IsNothrowMemberSwappable = | |
| std::bool_constant<IsMemberSwappableImpl<T>::kIsNothrow>; | |
| template <class T> | |
| class AnyInvTestBasic : public ::testing::Test {}; | |
| TYPED_TEST_SUITE_P(AnyInvTestBasic); | |
| TYPED_TEST_P(AnyInvTestBasic, DefaultConstruction) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| AnyInvType fun; | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| EXPECT_TRUE(std::is_nothrow_default_constructible_v<AnyInvType>); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, ConstructionNullptr) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| AnyInvType fun = nullptr; | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| EXPECT_TRUE((std::is_nothrow_constructible_v<AnyInvType, std::nullptr_t>)); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, ConstructionNullFunctionPtr) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using UnqualifiedFunType = typename TypeParam::UnqualifiedFunType; | |
| UnqualifiedFunType* const null_fun_ptr = nullptr; | |
| AnyInvType fun = null_fun_ptr; | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, ConstructionNullMemberFunctionPtr) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using MemFunPtrType = typename TypeParam::MemFunPtrType; | |
| const MemFunPtrType null_mem_fun_ptr = nullptr; | |
| AnyInvType fun = null_mem_fun_ptr; | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, ConstructionNullMemberObjectPtr) { | |
| using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType; | |
| using MemObjPtrType = typename TypeParam::MemObjPtrType; | |
| const MemObjPtrType null_mem_obj_ptr = nullptr; | |
| UnaryAnyInvType fun = null_mem_obj_ptr; | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, ConstructionMemberFunctionPtr) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| AnyInvType fun = &Int::MemberFunctionAdd; | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, ConstructionMemberObjectPtr) { | |
| using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType; | |
| UnaryAnyInvType fun = &Int::value; | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(13, TypeParam::ToUnaryThisParam(fun)(13)); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, ConstructionFunctionReferenceDecay) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| AnyInvType fun = add_function; | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, ConstructionCompatibleAnyInvocableEmpty) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using CompatibleAnyInvType = typename TypeParam::CompatibleAnyInvType; | |
| CompatibleAnyInvType other; | |
| AnyInvType fun = std::move(other); | |
| EXPECT_FALSE(static_cast<bool>(other)); // NOLINT | |
| EXPECT_EQ(other, nullptr); // NOLINT | |
| EXPECT_EQ(nullptr, other); // NOLINT | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, ConstructionCompatibleAnyInvocableNonempty) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using CompatibleAnyInvType = typename TypeParam::CompatibleAnyInvType; | |
| CompatibleAnyInvType other = &add_function; | |
| AnyInvType fun = std::move(other); | |
| EXPECT_FALSE(static_cast<bool>(other)); // NOLINT | |
| EXPECT_EQ(other, nullptr); // NOLINT | |
| EXPECT_EQ(nullptr, other); // NOLINT | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, ConversionToBool) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| { | |
| AnyInvType fun; | |
| // This tests contextually-convertible-to-bool. | |
| EXPECT_FALSE(fun ? true : false); // NOLINT | |
| // Make sure that the conversion is not implicit. | |
| EXPECT_TRUE((std::is_nothrow_constructible_v<bool, const AnyInvType&>)); | |
| EXPECT_FALSE((std::is_convertible_v<const AnyInvType&, bool>)); | |
| } | |
| { | |
| AnyInvType fun = &add_function; | |
| // This tests contextually-convertible-to-bool. | |
| EXPECT_TRUE(fun ? true : false); // NOLINT | |
| } | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, Invocation) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using FunType = typename TypeParam::FunType; | |
| using AnyInvCallType = MemberTypeOf<decltype(&AnyInvType::operator())>; | |
| // Make sure the function call operator of AnyInvocable always has the | |
| // type that was specified via the template argument. | |
| EXPECT_TRUE((std::is_same_v<AnyInvCallType, FunType>)); | |
| AnyInvType fun = &add_function; | |
| EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, InPlaceConstruction) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| AnyInvType fun(std::in_place_type<AddType>, 5); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, InPlaceConstructionInitializerList) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| AnyInvType fun(std::in_place_type<AddType>, {1, 2, 3, 4}, 5); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(39, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, InPlaceNullFunPtrConstruction) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using UnqualifiedFunType = typename TypeParam::UnqualifiedFunType; | |
| AnyInvType fun(std::in_place_type<UnqualifiedFunType*>, nullptr); | |
| // In-place construction does not lead to empty. | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, InPlaceNullFunPtrConstructionValueInit) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using UnqualifiedFunType = typename TypeParam::UnqualifiedFunType; | |
| AnyInvType fun(std::in_place_type<UnqualifiedFunType*>); | |
| // In-place construction does not lead to empty. | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, InPlaceNullMemFunPtrConstruction) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using MemFunPtrType = typename TypeParam::MemFunPtrType; | |
| AnyInvType fun(std::in_place_type<MemFunPtrType>, nullptr); | |
| // In-place construction does not lead to empty. | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, InPlaceNullMemFunPtrConstructionValueInit) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using MemFunPtrType = typename TypeParam::MemFunPtrType; | |
| AnyInvType fun(std::in_place_type<MemFunPtrType>); | |
| // In-place construction does not lead to empty. | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, InPlaceNullMemObjPtrConstruction) { | |
| using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType; | |
| using MemObjPtrType = typename TypeParam::MemObjPtrType; | |
| UnaryAnyInvType fun(std::in_place_type<MemObjPtrType>, nullptr); | |
| // In-place construction does not lead to empty. | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, InPlaceNullMemObjPtrConstructionValueInit) { | |
| using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType; | |
| using MemObjPtrType = typename TypeParam::MemObjPtrType; | |
| UnaryAnyInvType fun(std::in_place_type<MemObjPtrType>); | |
| // In-place construction does not lead to empty. | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, InPlaceVoidCovarianceConstruction) { | |
| using VoidAnyInvType = typename TypeParam::VoidAnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| VoidAnyInvType fun(std::in_place_type<AddType>, 5); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, MoveConstructionFromEmpty) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| absl_nullable AnyInvType source_fun; | |
| absl_nullable AnyInvType fun(std::move(source_fun)); | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| EXPECT_TRUE(std::is_nothrow_move_constructible_v<AnyInvType>); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, MoveConstructionFromNonEmpty) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| AnyInvType source_fun(std::in_place_type<AddType>, 5); | |
| AnyInvType fun(std::move(source_fun)); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| EXPECT_TRUE(std::is_nothrow_move_constructible_v<AnyInvType>); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, ComparisonWithNullptrEmpty) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| AnyInvType fun; | |
| EXPECT_TRUE(fun == nullptr); | |
| EXPECT_TRUE(nullptr == fun); | |
| EXPECT_FALSE(fun != nullptr); | |
| EXPECT_FALSE(nullptr != fun); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, ComparisonWithNullptrNonempty) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| AnyInvType fun(std::in_place_type<AddType>, 5); | |
| EXPECT_FALSE(fun == nullptr); | |
| EXPECT_FALSE(nullptr == fun); | |
| EXPECT_TRUE(fun != nullptr); | |
| EXPECT_TRUE(nullptr != fun); | |
| } | |
| TYPED_TEST_P(AnyInvTestBasic, ResultType) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using ExpectedResultType = typename TypeParam::ResultType; | |
| EXPECT_TRUE( | |
| (std::is_same_v<typename AnyInvType::result_type, ExpectedResultType>)); | |
| } | |
| template <class T> | |
| class AnyInvTestCombinatoric : public ::testing::Test {}; | |
| TYPED_TEST_SUITE_P(AnyInvTestCombinatoric); | |
| TYPED_TEST_P(AnyInvTestCombinatoric, MoveAssignEmptyEmptyLhsRhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| AnyInvType source_fun; | |
| AnyInvType fun; | |
| fun = std::move(source_fun); | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, MoveAssignEmptyLhsNonemptyRhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| AnyInvType source_fun(std::in_place_type<AddType>, 5); | |
| AnyInvType fun; | |
| fun = std::move(source_fun); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, MoveAssignNonemptyEmptyLhsRhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| AnyInvType source_fun; | |
| AnyInvType fun(std::in_place_type<AddType>, 5); | |
| fun = std::move(source_fun); | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, MoveAssignNonemptyLhsNonemptyRhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| AnyInvType source_fun(std::in_place_type<AddType>, 5); | |
| AnyInvType fun(std::in_place_type<AddType>, 20); | |
| fun = std::move(source_fun); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, SelfMoveAssignEmpty) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| AnyInvType source_fun; | |
| source_fun = std::move(source_fun); | |
| // This space intentionally left blank. | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, SelfMoveAssignNonempty) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| AnyInvType source_fun(std::in_place_type<AddType>, 5); | |
| source_fun = std::move(source_fun); | |
| // This space intentionally left blank. | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullptrEmptyLhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| AnyInvType fun; | |
| fun = nullptr; | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullFunctionPtrEmptyLhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using UnqualifiedFunType = typename TypeParam::UnqualifiedFunType; | |
| UnqualifiedFunType* const null_fun_ptr = nullptr; | |
| AnyInvType fun; | |
| fun = null_fun_ptr; | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullMemberFunctionPtrEmptyLhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using MemFunPtrType = typename TypeParam::MemFunPtrType; | |
| const MemFunPtrType null_mem_fun_ptr = nullptr; | |
| AnyInvType fun; | |
| fun = null_mem_fun_ptr; | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullMemberObjectPtrEmptyLhs) { | |
| using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType; | |
| using MemObjPtrType = typename TypeParam::MemObjPtrType; | |
| const MemObjPtrType null_mem_obj_ptr = nullptr; | |
| UnaryAnyInvType fun; | |
| fun = null_mem_obj_ptr; | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, AssignMemberFunctionPtrEmptyLhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| AnyInvType fun; | |
| fun = &Int::MemberFunctionAdd; | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, AssignMemberObjectPtrEmptyLhs) { | |
| using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType; | |
| UnaryAnyInvType fun; | |
| fun = &Int::value; | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(13, TypeParam::ToUnaryThisParam(fun)(13)); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, AssignFunctionReferenceDecayEmptyLhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| AnyInvType fun; | |
| fun = add_function; | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, | |
| AssignCompatibleAnyInvocableEmptyLhsEmptyRhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using CompatibleAnyInvType = typename TypeParam::CompatibleAnyInvType; | |
| CompatibleAnyInvType other; | |
| AnyInvType fun; | |
| fun = std::move(other); | |
| EXPECT_FALSE(static_cast<bool>(other)); // NOLINT | |
| EXPECT_EQ(other, nullptr); // NOLINT | |
| EXPECT_EQ(nullptr, other); // NOLINT | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, | |
| AssignCompatibleAnyInvocableEmptyLhsNonemptyRhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using CompatibleAnyInvType = typename TypeParam::CompatibleAnyInvType; | |
| CompatibleAnyInvType other = &add_function; | |
| AnyInvType fun; | |
| fun = std::move(other); | |
| EXPECT_FALSE(static_cast<bool>(other)); // NOLINT | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullptrNonemptyLhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| AnyInvType fun = &mult_function; | |
| fun = nullptr; | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullFunctionPtrNonemptyLhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using UnqualifiedFunType = typename TypeParam::UnqualifiedFunType; | |
| UnqualifiedFunType* const null_fun_ptr = nullptr; | |
| AnyInvType fun = &mult_function; | |
| fun = null_fun_ptr; | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullMemberFunctionPtrNonemptyLhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using MemFunPtrType = typename TypeParam::MemFunPtrType; | |
| const MemFunPtrType null_mem_fun_ptr = nullptr; | |
| AnyInvType fun = &mult_function; | |
| fun = null_mem_fun_ptr; | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, AssignNullMemberObjectPtrNonemptyLhs) { | |
| using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType; | |
| using MemObjPtrType = typename TypeParam::MemObjPtrType; | |
| const MemObjPtrType null_mem_obj_ptr = nullptr; | |
| UnaryAnyInvType fun = &square_function; | |
| fun = null_mem_obj_ptr; | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, AssignMemberFunctionPtrNonemptyLhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| AnyInvType fun = &mult_function; | |
| fun = &Int::MemberFunctionAdd; | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, AssignMemberObjectPtrNonemptyLhs) { | |
| using UnaryAnyInvType = typename TypeParam::UnaryAnyInvType; | |
| UnaryAnyInvType fun = &square_function; | |
| fun = &Int::value; | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(13, TypeParam::ToUnaryThisParam(fun)(13)); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, AssignFunctionReferenceDecayNonemptyLhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| AnyInvType fun = &mult_function; | |
| fun = add_function; | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, | |
| AssignCompatibleAnyInvocableNonemptyLhsEmptyRhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using CompatibleAnyInvType = typename TypeParam::CompatibleAnyInvType; | |
| CompatibleAnyInvType other; | |
| AnyInvType fun = &mult_function; | |
| fun = std::move(other); | |
| EXPECT_FALSE(static_cast<bool>(other)); // NOLINT | |
| EXPECT_EQ(other, nullptr); // NOLINT | |
| EXPECT_EQ(nullptr, other); // NOLINT | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, | |
| AssignCompatibleAnyInvocableNonemptyLhsNonemptyRhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using CompatibleAnyInvType = typename TypeParam::CompatibleAnyInvType; | |
| CompatibleAnyInvType other = &add_function; | |
| AnyInvType fun = &mult_function; | |
| fun = std::move(other); | |
| EXPECT_FALSE(static_cast<bool>(other)); // NOLINT | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(24, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, SwapEmptyLhsEmptyRhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| // Swap idiom | |
| { | |
| AnyInvType fun; | |
| AnyInvType other; | |
| using std::swap; | |
| swap(fun, other); | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| EXPECT_FALSE(static_cast<bool>(other)); | |
| EXPECT_TRUE( | |
| std::is_nothrow_swappable_v<AnyInvType>); | |
| } | |
| // Member swap | |
| { | |
| AnyInvType fun; | |
| AnyInvType other; | |
| fun.swap(other); | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| EXPECT_FALSE(static_cast<bool>(other)); | |
| EXPECT_TRUE(IsNothrowMemberSwappable<AnyInvType>::value); | |
| } | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, SwapEmptyLhsNonemptyRhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| // Swap idiom | |
| { | |
| AnyInvType fun; | |
| AnyInvType other(std::in_place_type<AddType>, 5); | |
| using std::swap; | |
| swap(fun, other); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_FALSE(static_cast<bool>(other)); | |
| EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| EXPECT_TRUE( | |
| std::is_nothrow_swappable_v<AnyInvType>); | |
| } | |
| // Member swap | |
| { | |
| AnyInvType fun; | |
| AnyInvType other(std::in_place_type<AddType>, 5); | |
| fun.swap(other); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_FALSE(static_cast<bool>(other)); | |
| EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| EXPECT_TRUE(IsNothrowMemberSwappable<AnyInvType>::value); | |
| } | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, SwapNonemptyLhsEmptyRhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| // Swap idiom | |
| { | |
| AnyInvType fun(std::in_place_type<AddType>, 5); | |
| AnyInvType other; | |
| using std::swap; | |
| swap(fun, other); | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| EXPECT_TRUE(static_cast<bool>(other)); | |
| EXPECT_EQ(29, TypeParam::ToThisParam(other)(7, 8, 9).value); | |
| EXPECT_TRUE( | |
| std::is_nothrow_swappable_v<AnyInvType>); | |
| } | |
| // Member swap | |
| { | |
| AnyInvType fun(std::in_place_type<AddType>, 5); | |
| AnyInvType other; | |
| fun.swap(other); | |
| EXPECT_FALSE(static_cast<bool>(fun)); | |
| EXPECT_TRUE(static_cast<bool>(other)); | |
| EXPECT_EQ(29, TypeParam::ToThisParam(other)(7, 8, 9).value); | |
| EXPECT_TRUE(IsNothrowMemberSwappable<AnyInvType>::value); | |
| } | |
| } | |
| TYPED_TEST_P(AnyInvTestCombinatoric, SwapNonemptyLhsNonemptyRhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| // Swap idiom | |
| { | |
| AnyInvType fun(std::in_place_type<AddType>, 5); | |
| AnyInvType other(std::in_place_type<AddType>, 6); | |
| using std::swap; | |
| swap(fun, other); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_TRUE(static_cast<bool>(other)); | |
| EXPECT_EQ(30, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| EXPECT_EQ(29, TypeParam::ToThisParam(other)(7, 8, 9).value); | |
| EXPECT_TRUE( | |
| std::is_nothrow_swappable_v<AnyInvType>); | |
| } | |
| // Member swap | |
| { | |
| AnyInvType fun(std::in_place_type<AddType>, 5); | |
| AnyInvType other(std::in_place_type<AddType>, 6); | |
| fun.swap(other); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_TRUE(static_cast<bool>(other)); | |
| EXPECT_EQ(30, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| EXPECT_EQ(29, TypeParam::ToThisParam(other)(7, 8, 9).value); | |
| EXPECT_TRUE(IsNothrowMemberSwappable<AnyInvType>::value); | |
| } | |
| } | |
| template <class T> | |
| class AnyInvTestMovable : public ::testing::Test {}; | |
| TYPED_TEST_SUITE_P(AnyInvTestMovable); | |
| TYPED_TEST_P(AnyInvTestMovable, ConversionConstructionUserDefinedType) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| AnyInvType fun(AddType(5)); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| } | |
| TYPED_TEST_P(AnyInvTestMovable, ConversionConstructionVoidCovariance) { | |
| using VoidAnyInvType = typename TypeParam::VoidAnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| VoidAnyInvType fun(AddType(5)); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| } | |
| TYPED_TEST_P(AnyInvTestMovable, ConversionAssignUserDefinedTypeEmptyLhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| AnyInvType fun; | |
| fun = AddType(5); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| } | |
| TYPED_TEST_P(AnyInvTestMovable, ConversionAssignUserDefinedTypeNonemptyLhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| AnyInvType fun = &add_function; | |
| fun = AddType(5); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| } | |
| TYPED_TEST_P(AnyInvTestMovable, ConversionAssignVoidCovariance) { | |
| using VoidAnyInvType = typename TypeParam::VoidAnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| VoidAnyInvType fun; | |
| fun = AddType(5); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| } | |
| template <class T> | |
| class AnyInvTestNoexceptFalse : public ::testing::Test {}; | |
| TYPED_TEST_SUITE_P(AnyInvTestNoexceptFalse); | |
| TYPED_TEST_P(AnyInvTestNoexceptFalse, ConversionConstructionConstraints) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| EXPECT_TRUE( | |
| (std::is_constructible_v< | |
| AnyInvType, typename TypeParam::AnyInvocableFunTypeNotNoexcept*>)); | |
| EXPECT_FALSE( | |
| (std::is_constructible_v<AnyInvType, | |
| typename TypeParam::IncompatibleInvocable>)); | |
| } | |
| TYPED_TEST_P(AnyInvTestNoexceptFalse, ConversionAssignConstraints) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| EXPECT_TRUE( | |
| (std::is_assignable_v< | |
| AnyInvType&, typename TypeParam::AnyInvocableFunTypeNotNoexcept*>)); | |
| EXPECT_FALSE( | |
| (std::is_assignable_v<AnyInvType&, | |
| typename TypeParam::IncompatibleInvocable>)); | |
| } | |
| template <class T> | |
| class AnyInvTestNoexceptTrue : public ::testing::Test {}; | |
| TYPED_TEST_SUITE_P(AnyInvTestNoexceptTrue); | |
| TYPED_TEST_P(AnyInvTestNoexceptTrue, ConversionConstructionConstraints) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| EXPECT_FALSE( | |
| (std::is_constructible_v< | |
| AnyInvType, typename TypeParam::AnyInvocableFunTypeNotNoexcept*>)); | |
| EXPECT_FALSE( | |
| (std::is_constructible_v<AnyInvType, | |
| typename TypeParam::IncompatibleInvocable>)); | |
| } | |
| TYPED_TEST_P(AnyInvTestNoexceptTrue, ConversionAssignConstraints) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| EXPECT_FALSE( | |
| (std::is_assignable_v< | |
| AnyInvType&, typename TypeParam::AnyInvocableFunTypeNotNoexcept*>)); | |
| EXPECT_FALSE( | |
| (std::is_assignable_v<AnyInvType&, | |
| typename TypeParam::IncompatibleInvocable>)); | |
| } | |
| template <class T> | |
| class AnyInvTestNonRvalue : public ::testing::Test {}; | |
| TYPED_TEST_SUITE_P(AnyInvTestNonRvalue); | |
| TYPED_TEST_P(AnyInvTestNonRvalue, ConversionConstructionReferenceWrapper) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| AddType add(4); | |
| AnyInvType fun = std::ref(add); | |
| add.state = 5; | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).value); | |
| } | |
| TYPED_TEST_P(AnyInvTestNonRvalue, NonMoveableResultType) { | |
| // Define a result type that cannot be copy- or move-constructed. | |
| struct Result { | |
| int x; | |
| explicit Result(const int x_in) : x(x_in) {} | |
| Result(Result&&) = delete; | |
| }; | |
| static_assert(!std::is_move_constructible_v<Result>); | |
| static_assert(!std::is_copy_constructible_v<Result>); | |
| // Assumption check: it should nevertheless be possible to use functors that | |
| // return a Result struct according to the language rules. | |
| const auto return_17 = []() noexcept { return Result(17); }; | |
| EXPECT_EQ(17, return_17().x); | |
| // Just like plain functors, it should work fine to use an AnyInvocable that | |
| // returns the non-moveable type. | |
| using UnqualifiedFun = | |
| std::conditional_t<TypeParam::kIsNoexcept, Result() noexcept, Result()>; | |
| using Fun = | |
| GiveQualifiersToFun<typename TypeParam::Qualifiers, UnqualifiedFun>; | |
| AnyInvocable<Fun> any_inv(return_17); | |
| EXPECT_EQ(17, any_inv().x); | |
| } | |
| TYPED_TEST_P(AnyInvTestNonRvalue, ConversionAssignReferenceWrapperEmptyLhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| AddType add(4); | |
| AnyInvType fun; | |
| fun = std::ref(add); | |
| add.state = 5; | |
| EXPECT_TRUE((std::is_nothrow_assignable_v<AnyInvType&, | |
| std::reference_wrapper<AddType>>)); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).value); | |
| } | |
| TYPED_TEST_P(AnyInvTestNonRvalue, ConversionAssignReferenceWrapperNonemptyLhs) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| AddType add(4); | |
| AnyInvType fun = &mult_function; | |
| fun = std::ref(add); | |
| add.state = 5; | |
| EXPECT_TRUE((std::is_nothrow_assignable_v<AnyInvType&, | |
| std::reference_wrapper<AddType>>)); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(29, TypeParam::ToThisParam(fun)(7, 8, 9).value); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).value); | |
| } | |
| template <class T> | |
| class AnyInvTestRvalue : public ::testing::Test {}; | |
| TYPED_TEST_SUITE_P(AnyInvTestRvalue); | |
| TYPED_TEST_P(AnyInvTestRvalue, ConversionConstructionReferenceWrapper) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| EXPECT_FALSE( | |
| (std::is_convertible_v<std::reference_wrapper<AddType>, AnyInvType>)); | |
| } | |
| TYPED_TEST_P(AnyInvTestRvalue, NonMoveableResultType) { | |
| // Define a result type that cannot be copy- or move-constructed. | |
| struct Result { | |
| int x; | |
| explicit Result(const int x_in) : x(x_in) {} | |
| Result(Result&&) = delete; | |
| }; | |
| static_assert(!std::is_move_constructible_v<Result>); | |
| static_assert(!std::is_copy_constructible_v<Result>); | |
| // Assumption check: it should nevertheless be possible to use functors that | |
| // return a Result struct according to the language rules. | |
| const auto return_17 = []() noexcept { return Result(17); }; | |
| EXPECT_EQ(17, return_17().x); | |
| // Just like plain functors, it should work fine to use an AnyInvocable that | |
| // returns the non-moveable type. | |
| using UnqualifiedFun = | |
| std::conditional_t<TypeParam::kIsNoexcept, Result() noexcept, Result()>; | |
| using Fun = | |
| GiveQualifiersToFun<typename TypeParam::Qualifiers, UnqualifiedFun>; | |
| EXPECT_EQ(17, AnyInvocable<Fun>(return_17)().x); | |
| } | |
| TYPED_TEST_P(AnyInvTestRvalue, ConversionAssignReferenceWrapper) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| EXPECT_FALSE( | |
| (std::is_assignable_v<AnyInvType&, std::reference_wrapper<AddType>>)); | |
| } | |
| TYPED_TEST_P(AnyInvTestRvalue, NonConstCrashesOnSecondCall) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| using AddType = typename TypeParam::AddType; | |
| AnyInvType fun(std::in_place_type<AddType>, 5); | |
| EXPECT_TRUE(static_cast<bool>(fun)); | |
| std::move(fun)(7, 8, 9); | |
| // Ensure we're still valid | |
| EXPECT_TRUE(static_cast<bool>(fun)); // NOLINT(bugprone-use-after-move) | |
| #if !defined(NDEBUG) | |
| EXPECT_DEATH_IF_SUPPORTED(std::move(fun)(7, 8, 9), ""); | |
| #endif | |
| } | |
| // Ensure that any qualifiers (in particular &&-qualifiers) do not affect | |
| // when the destructor is actually run. | |
| TYPED_TEST_P(AnyInvTestRvalue, QualifierIndependentObjectLifetime) { | |
| using AnyInvType = typename TypeParam::AnyInvType; | |
| auto refs = std::make_shared<std::nullptr_t>(); | |
| { | |
| AnyInvType fun([refs](auto&&...) noexcept { return 0; }); | |
| EXPECT_GT(refs.use_count(), 1); | |
| std::move(fun)(7, 8, 9); | |
| // Ensure destructor hasn't run even if rref-qualified | |
| EXPECT_GT(refs.use_count(), 1); | |
| } | |
| EXPECT_EQ(refs.use_count(), 1); | |
| } | |
| // NOTE: This test suite originally attempted to enumerate all possible | |
| // combinations of type properties but the build-time started getting too large. | |
| // Instead, it is now assumed that certain parameters are orthogonal and so | |
| // some combinations are elided. | |
| // A metafunction to form a TypeList of all cv and non-rvalue ref combinations, | |
| // coupled with all of the other explicitly specified parameters. | |
| template <Movable Mov, Destructible Dest, NothrowCall CallExceptionSpec, | |
| ObjSize Size, ObjAlign Align> | |
| using NonRvalueQualifiedTestParams = ::testing::Types< // | |
| TestParams<Mov, Dest, _, CallExceptionSpec, Size, Align>, // | |
| TestParams<Mov, Dest, const _, CallExceptionSpec, Size, Align>, // | |
| TestParams<Mov, Dest, _&, CallExceptionSpec, Size, Align>, // | |
| TestParams<Mov, Dest, const _&, CallExceptionSpec, Size, Align>>; | |
| // A metafunction to form a TypeList of const and non-const rvalue ref | |
| // qualifiers, coupled with all of the other explicitly specified parameters. | |
| template <Movable Mov, Destructible Dest, NothrowCall CallExceptionSpec, | |
| ObjSize Size, ObjAlign Align> | |
| using RvalueQualifiedTestParams = ::testing::Types< | |
| TestParams<Mov, Dest, _&&, CallExceptionSpec, Size, Align>, // | |
| TestParams<Mov, Dest, const _&&, CallExceptionSpec, Size, Align> // | |
| >; | |
| // All qualifier combinations and a noexcept function type | |
| using TestParameterListNonRvalueQualifiersNothrowCall = | |
| NonRvalueQualifiedTestParams<Movable::trivial, Destructible::trivial, | |
| NothrowCall::yes, ObjSize::small, | |
| ObjAlign::normal>; | |
| using TestParameterListRvalueQualifiersNothrowCall = | |
| RvalueQualifiedTestParams<Movable::trivial, Destructible::trivial, | |
| NothrowCall::yes, ObjSize::small, | |
| ObjAlign::normal>; | |
| // All qualifier combinations and a non-noexcept function type | |
| using TestParameterListNonRvalueQualifiersCallMayThrow = | |
| NonRvalueQualifiedTestParams<Movable::trivial, Destructible::trivial, | |
| NothrowCall::no, ObjSize::small, | |
| ObjAlign::normal>; | |
| using TestParameterListRvalueQualifiersCallMayThrow = | |
| RvalueQualifiedTestParams<Movable::trivial, Destructible::trivial, | |
| NothrowCall::no, ObjSize::small, | |
| ObjAlign::normal>; | |
| // Lists of various cases that should lead to remote storage | |
| using TestParameterListRemoteMovable = ::testing::Types< | |
| // "Normal" aligned types that are large and have trivial destructors | |
| TestParams<Movable::trivial, Destructible::trivial, _, NothrowCall::no, | |
| ObjSize::large, ObjAlign::normal>, // | |
| TestParams<Movable::nothrow, Destructible::trivial, _, NothrowCall::no, | |
| ObjSize::large, ObjAlign::normal>, // | |
| TestParams<Movable::yes, Destructible::trivial, _, NothrowCall::no, | |
| ObjSize::small, ObjAlign::normal>, // | |
| TestParams<Movable::yes, Destructible::trivial, _, NothrowCall::no, | |
| ObjSize::large, ObjAlign::normal>, // | |
| // Same as above but with non-trivial destructors | |
| TestParams<Movable::trivial, Destructible::nothrow, _, NothrowCall::no, | |
| ObjSize::large, ObjAlign::normal>, // | |
| TestParams<Movable::nothrow, Destructible::nothrow, _, NothrowCall::no, | |
| ObjSize::large, ObjAlign::normal>, // | |
| TestParams<Movable::yes, Destructible::nothrow, _, NothrowCall::no, | |
| ObjSize::small, ObjAlign::normal>, // | |
| TestParams<Movable::yes, Destructible::nothrow, _, NothrowCall::no, | |
| ObjSize::large, ObjAlign::normal> // | |
| // Types that must use remote storage because of a large alignment. | |
| , | |
| TestParams<Movable::trivial, Destructible::trivial, _, NothrowCall::no, | |
| ObjSize::small, ObjAlign::large>, // | |
| TestParams<Movable::nothrow, Destructible::trivial, _, NothrowCall::no, | |
| ObjSize::small, ObjAlign::large>, // | |
| TestParams<Movable::trivial, Destructible::nothrow, _, NothrowCall::no, | |
| ObjSize::small, ObjAlign::large>, // | |
| TestParams<Movable::nothrow, Destructible::nothrow, _, NothrowCall::no, | |
| ObjSize::small, ObjAlign::large> // | |
| >; | |
| using TestParameterListRemoteNonMovable = ::testing::Types< | |
| // "Normal" aligned types that are large and have trivial destructors | |
| TestParams<Movable::no, Destructible::trivial, _, NothrowCall::no, | |
| ObjSize::small, ObjAlign::normal>, // | |
| TestParams<Movable::no, Destructible::trivial, _, NothrowCall::no, | |
| ObjSize::large, ObjAlign::normal>, // | |
| // Same as above but with non-trivial destructors | |
| TestParams<Movable::no, Destructible::nothrow, _, NothrowCall::no, | |
| ObjSize::small, ObjAlign::normal>, // | |
| TestParams<Movable::no, Destructible::nothrow, _, NothrowCall::no, | |
| ObjSize::large, ObjAlign::normal> // | |
| >; | |
| // Parameters that lead to local storage | |
| using TestParameterListLocal = ::testing::Types< | |
| // Types that meet the requirements and have trivial destructors | |
| TestParams<Movable::trivial, Destructible::trivial, _, NothrowCall::no, | |
| ObjSize::small, ObjAlign::normal>, // | |
| TestParams<Movable::nothrow, Destructible::trivial, _, NothrowCall::no, | |
| ObjSize::small, ObjAlign::normal>, // | |
| // Same as above but with non-trivial destructors | |
| TestParams<Movable::trivial, Destructible::trivial, _, NothrowCall::no, | |
| ObjSize::small, ObjAlign::normal>, // | |
| TestParams<Movable::nothrow, Destructible::trivial, _, NothrowCall::no, | |
| ObjSize::small, ObjAlign::normal> // | |
| >; | |
| // All of the tests that are run for every possible combination of types. | |
| REGISTER_TYPED_TEST_SUITE_P( | |
| AnyInvTestBasic, DefaultConstruction, ConstructionNullptr, | |
| ConstructionNullFunctionPtr, ConstructionNullMemberFunctionPtr, | |
| ConstructionNullMemberObjectPtr, ConstructionMemberFunctionPtr, | |
| ConstructionMemberObjectPtr, ConstructionFunctionReferenceDecay, | |
| ConstructionCompatibleAnyInvocableEmpty, | |
| ConstructionCompatibleAnyInvocableNonempty, InPlaceConstruction, | |
| ConversionToBool, Invocation, InPlaceConstructionInitializerList, | |
| InPlaceNullFunPtrConstruction, InPlaceNullFunPtrConstructionValueInit, | |
| InPlaceNullMemFunPtrConstruction, InPlaceNullMemFunPtrConstructionValueInit, | |
| InPlaceNullMemObjPtrConstruction, InPlaceNullMemObjPtrConstructionValueInit, | |
| InPlaceVoidCovarianceConstruction, MoveConstructionFromEmpty, | |
| MoveConstructionFromNonEmpty, ComparisonWithNullptrEmpty, | |
| ComparisonWithNullptrNonempty, ResultType); | |
| // Tests for functions that take two operands. | |
| REGISTER_TYPED_TEST_SUITE_P( | |
| AnyInvTestCombinatoric, MoveAssignEmptyEmptyLhsRhs, | |
| MoveAssignEmptyLhsNonemptyRhs, MoveAssignNonemptyEmptyLhsRhs, | |
| MoveAssignNonemptyLhsNonemptyRhs, SelfMoveAssignEmpty, | |
| SelfMoveAssignNonempty, AssignNullptrEmptyLhs, | |
| AssignNullFunctionPtrEmptyLhs, AssignNullMemberFunctionPtrEmptyLhs, | |
| AssignNullMemberObjectPtrEmptyLhs, AssignMemberFunctionPtrEmptyLhs, | |
| AssignMemberObjectPtrEmptyLhs, AssignFunctionReferenceDecayEmptyLhs, | |
| AssignCompatibleAnyInvocableEmptyLhsEmptyRhs, | |
| AssignCompatibleAnyInvocableEmptyLhsNonemptyRhs, AssignNullptrNonemptyLhs, | |
| AssignNullFunctionPtrNonemptyLhs, AssignNullMemberFunctionPtrNonemptyLhs, | |
| AssignNullMemberObjectPtrNonemptyLhs, AssignMemberFunctionPtrNonemptyLhs, | |
| AssignMemberObjectPtrNonemptyLhs, AssignFunctionReferenceDecayNonemptyLhs, | |
| AssignCompatibleAnyInvocableNonemptyLhsEmptyRhs, | |
| AssignCompatibleAnyInvocableNonemptyLhsNonemptyRhs, SwapEmptyLhsEmptyRhs, | |
| SwapEmptyLhsNonemptyRhs, SwapNonemptyLhsEmptyRhs, | |
| SwapNonemptyLhsNonemptyRhs); | |
| REGISTER_TYPED_TEST_SUITE_P(AnyInvTestMovable, | |
| ConversionConstructionUserDefinedType, | |
| ConversionConstructionVoidCovariance, | |
| ConversionAssignUserDefinedTypeEmptyLhs, | |
| ConversionAssignUserDefinedTypeNonemptyLhs, | |
| ConversionAssignVoidCovariance); | |
| REGISTER_TYPED_TEST_SUITE_P(AnyInvTestNoexceptFalse, | |
| ConversionConstructionConstraints, | |
| ConversionAssignConstraints); | |
| REGISTER_TYPED_TEST_SUITE_P(AnyInvTestNoexceptTrue, | |
| ConversionConstructionConstraints, | |
| ConversionAssignConstraints); | |
| REGISTER_TYPED_TEST_SUITE_P(AnyInvTestNonRvalue, | |
| ConversionConstructionReferenceWrapper, | |
| NonMoveableResultType, | |
| ConversionAssignReferenceWrapperEmptyLhs, | |
| ConversionAssignReferenceWrapperNonemptyLhs); | |
| REGISTER_TYPED_TEST_SUITE_P(AnyInvTestRvalue, | |
| ConversionConstructionReferenceWrapper, | |
| NonMoveableResultType, | |
| ConversionAssignReferenceWrapper, | |
| NonConstCrashesOnSecondCall, | |
| QualifierIndependentObjectLifetime); | |
| // Minimal SFINAE testing for platforms where we can't run the tests, but we can | |
| // build binaries for. | |
| static_assert(std::is_convertible_v<void (*)(), absl::AnyInvocable<void() &&>>); | |
| static_assert(!std::is_convertible_v<void*, absl::AnyInvocable<void() &&>>); | |
| } // namespace absl_any_invocable_test | |
| #endif // ABSL_FUNCTIONAL_ANY_INVOCABLE_TEST_H_ |