diff options
Diffstat (limited to 'absl/functional/any_invocable_test.cc')
-rw-r--r-- | absl/functional/any_invocable_test.cc | 1696 |
1 files changed, 1696 insertions, 0 deletions
diff --git a/absl/functional/any_invocable_test.cc b/absl/functional/any_invocable_test.cc new file mode 100644 index 00000000..fb5e7792 --- /dev/null +++ b/absl/functional/any_invocable_test.cc @@ -0,0 +1,1696 @@ +// 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. + +#include "absl/functional/any_invocable.h" + +#include <cstddef> +#include <initializer_list> +#include <numeric> +#include <type_traits> + +#include "gtest/gtest.h" +#include "absl/base/config.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 { + +// Helper macro used to avoid spelling `noexcept` in language versions older +// than C++17, where it is not part of the type system, in order to avoid +// compilation failures and internal compiler errors. +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L +#define ABSL_INTERNAL_NOEXCEPT_SPEC(noex) noexcept(noex) +#else +#define ABSL_INTERNAL_NOEXCEPT_SPEC(noex) +#endif + +// A dummy type we use when passing qualifiers to metafunctions +struct _ {}; + +template <class T> +struct Wrapper { + template <class U, + class = absl::enable_if_t<std::is_convertible<U, T>::value>> + 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<Wrapper<absl::AnyInvocable<void()>>, + Wrapper<absl::AnyInvocable<void()>>>::value, + ""); + +// 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<This>::value, ""); + using type = + absl::conditional_t<std::is_const<Qualifiers>::value, 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<This>::value, ""); + using type = + absl::conditional_t<std::is_const<Qualifiers>::value, 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 = + absl::conditional_t<std::is_const<T>::value, R(P...) const, R(P...)>; +}; + +template <class T, class R, class... P> +struct GiveQualifiersToFunImpl<T&, R(P...)> { + using type = + absl::conditional_t<std::is_const<T>::value, R(P...) const&, R(P...)&>; +}; + +template <class T, class R, class... P> +struct GiveQualifiersToFunImpl<T&&, R(P...)> { + using type = + absl::conditional_t<std::is_const<T>::value, R(P...) const&&, R(P...) &&>; +}; + +// If noexcept is a part of the type system, then provide the noexcept forms. +#if defined(__cpp_noexcept_function_type) + +template <class T, class R, class... P> +struct GiveQualifiersToFunImpl<T, R(P...) noexcept> { + using type = absl::conditional_t<std::is_const<T>::value, + R(P...) const noexcept, R(P...) noexcept>; +}; + +template <class T, class R, class... P> +struct GiveQualifiersToFunImpl<T&, R(P...) noexcept> { + using type = + absl::conditional_t<std::is_const<T>::value, R(P...) const & noexcept, + R(P...) & noexcept>; +}; + +template <class T, class R, class... P> +struct GiveQualifiersToFunImpl<T&&, R(P...) noexcept> { + using type = + absl::conditional_t<std::is_const<T>::value, R(P...) const && noexcept, + R(P...) && noexcept>; +}; + +#endif // defined(__cpp_noexcept_function_type) + +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 \ + ABSL_INTERNAL_NOEXCEPT_SPEC(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 \ + ABSL_INTERNAL_NOEXCEPT_SPEC(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 +Int add_function(Int&& a, int b, int c) noexcept { return a.value + b + c; } + +Int mult_function(Int&& a, int b, int c) noexcept { return a.value * b * c; } + +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<Qual>::value; + 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&&) + ABSL_INTERNAL_NOEXCEPT_SPEC(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 = + typename std::conditional<kIsNoexcept, Int(Int, const int&, int) noexcept, + Int(Int, const int&, int)>::type; + using FunType = GiveQualifiersToFun<Qualifiers, UnqualifiedFunType>; + using MemFunPtrType = + typename std::conditional<kIsNoexcept, + Int (Int::*)(const int&, int) noexcept, + Int (Int::*)(const int&, int)>::type; + 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 = + typename std::conditional<kIsNoexcept, + void(Int, const int&, int) noexcept, + void(Int, const int&, int)>::type; + 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 = + absl::conditional_t<std::is_rvalue_reference<Qual>::value, + GiveQualifiersToFun<const _&&, UnqualifiedFunType>, + GiveQualifiersToFun<const _&, UnqualifiedFunType>>; + + using CompatibleAnyInvType = AnyInvocable<CompatibleAnyInvocableFunType>; + + using IncompatibleInvocable = + absl::conditional_t<std::is_rvalue_reference<Qual>::value, + 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, absl::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::integral_constant<bool, 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<AnyInvType>::value); +} + +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<AnyInvType, std::nullptr_t>::value)); +} + +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<bool, const AnyInvType&>::value)); + EXPECT_FALSE((std::is_convertible<const AnyInvType&, bool>::value)); + } + + { + 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<AnyInvCallType, FunType>::value)); + + 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(absl::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(absl::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(absl::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(absl::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(absl::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(absl::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(absl::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(absl::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(absl::in_place_type<AddType>, 5); + + EXPECT_TRUE(static_cast<bool>(fun)); +} + +TYPED_TEST_P(AnyInvTestBasic, MoveConstructionFromEmpty) { + using AnyInvType = typename TypeParam::AnyInvType; + + AnyInvType source_fun; + AnyInvType fun(std::move(source_fun)); + + EXPECT_FALSE(static_cast<bool>(fun)); + + EXPECT_TRUE(std::is_nothrow_move_constructible<AnyInvType>::value); +} + +TYPED_TEST_P(AnyInvTestBasic, MoveConstructionFromNonEmpty) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + AnyInvType source_fun(absl::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<AnyInvType>::value); +} + +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(absl::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<typename AnyInvType::result_type, + ExpectedResultType>::value)); +} + +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(absl::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(absl::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(absl::in_place_type<AddType>, 5); + AnyInvType fun(absl::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(absl::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( + absl::type_traits_internal::IsNothrowSwappable<AnyInvType>::value); + } + + // 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(absl::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( + absl::type_traits_internal::IsNothrowSwappable<AnyInvType>::value); + } + + // Member swap + { + AnyInvType fun; + AnyInvType other(absl::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(absl::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( + absl::type_traits_internal::IsNothrowSwappable<AnyInvType>::value); + } + + // Member swap + { + AnyInvType fun(absl::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(absl::in_place_type<AddType>, 5); + AnyInvType other(absl::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( + absl::type_traits_internal::IsNothrowSwappable<AnyInvType>::value); + } + + // Member swap + { + AnyInvType fun(absl::in_place_type<AddType>, 5); + AnyInvType other(absl::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); + + EXPECT_TRUE(static_cast<bool>(fun)); + EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).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); + + EXPECT_TRUE(static_cast<bool>(fun)); + EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).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); + + EXPECT_TRUE(static_cast<bool>(fun)); + EXPECT_EQ(38, TypeParam::ToThisParam(fun)(10, 11, 12).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< + AnyInvType, + typename TypeParam::AnyInvocableFunTypeNotNoexcept*>::value)); + EXPECT_FALSE(( + std::is_constructible<AnyInvType, + typename TypeParam::IncompatibleInvocable>::value)); +} + +TYPED_TEST_P(AnyInvTestNoexceptFalse, ConversionAssignConstraints) { + using AnyInvType = typename TypeParam::AnyInvType; + + EXPECT_TRUE((std::is_assignable< + AnyInvType&, + typename TypeParam::AnyInvocableFunTypeNotNoexcept*>::value)); + EXPECT_FALSE( + (std::is_assignable<AnyInvType&, + typename TypeParam::IncompatibleInvocable>::value)); +} + +template <class T> +class AnyInvTestNoexceptTrue : public ::testing::Test {}; + +TYPED_TEST_SUITE_P(AnyInvTestNoexceptTrue); + +TYPED_TEST_P(AnyInvTestNoexceptTrue, ConversionConstructionConstraints) { +#if ABSL_INTERNAL_CPLUSPLUS_LANG < 201703L + GTEST_SKIP() << "Noexcept was not part of the type system before C++17."; +#else + using AnyInvType = typename TypeParam::AnyInvType; + +// TODO(b/217761454): Fix this and re-enable for MSVC. +#ifndef _MSC_VER + EXPECT_FALSE((std::is_constructible< + AnyInvType, + typename TypeParam::AnyInvocableFunTypeNotNoexcept*>::value)); +#endif + EXPECT_FALSE(( + std::is_constructible<AnyInvType, + typename TypeParam::IncompatibleInvocable>::value)); +#endif +} + +TYPED_TEST_P(AnyInvTestNoexceptTrue, ConversionAssignConstraints) { +#if ABSL_INTERNAL_CPLUSPLUS_LANG < 201703L + GTEST_SKIP() << "Noexcept was not part of the type system before C++17."; +#else + using AnyInvType = typename TypeParam::AnyInvType; + +// TODO(b/217761454): Fix this and re-enable for MSVC. +#ifndef _MSC_VER + EXPECT_FALSE((std::is_assignable< + AnyInvType&, + typename TypeParam::AnyInvocableFunTypeNotNoexcept*>::value)); +#endif + EXPECT_FALSE( + (std::is_assignable<AnyInvType&, + typename TypeParam::IncompatibleInvocable>::value)); +#endif +} + +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) { +#if ABSL_INTERNAL_CPLUSPLUS_LANG < 201703L + GTEST_SKIP() << "Copy/move elision was not standard before C++17"; +#else + // 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<Result>::value, ""); + static_assert(!std::is_copy_constructible<Result>::value, ""); + + // 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 = + absl::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); +#endif +} + +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<AnyInvType&, + std::reference_wrapper<AddType>>::value)); + + 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<AnyInvType&, + std::reference_wrapper<AddType>>::value)); + + 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<std::reference_wrapper<AddType>, AnyInvType>::value)); +} + +TYPED_TEST_P(AnyInvTestRvalue, NonMoveableResultType) { +#if ABSL_INTERNAL_CPLUSPLUS_LANG < 201703L + GTEST_SKIP() << "Copy/move elision was not standard before C++17"; +#else + // 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<Result>::value, ""); + static_assert(!std::is_copy_constructible<Result>::value, ""); + + // 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 = + absl::conditional_t<TypeParam::kIsNoexcept, Result() noexcept, Result()>; + + using Fun = + GiveQualifiersToFun<typename TypeParam::Qualifiers, UnqualifiedFun>; + + EXPECT_EQ(17, AnyInvocable<Fun>(return_17)().x); +#endif +} + +TYPED_TEST_P(AnyInvTestRvalue, ConversionAssignReferenceWrapper) { + using AnyInvType = typename TypeParam::AnyInvType; + using AddType = typename TypeParam::AddType; + + EXPECT_FALSE(( + std::is_assignable<AnyInvType&, std::reference_wrapper<AddType>>::value)); +} + +// 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> // + +// Dynamic memory allocation for over-aligned data was introduced in C++17. +// See https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0035r4.html +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L + // 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> // +#endif + >; +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); + +INSTANTIATE_TYPED_TEST_SUITE_P( + NonRvalueCallMayThrow, AnyInvTestBasic, + TestParameterListNonRvalueQualifiersCallMayThrow); +INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallMayThrow, AnyInvTestBasic, + TestParameterListRvalueQualifiersCallMayThrow); + +INSTANTIATE_TYPED_TEST_SUITE_P(RemoteMovable, AnyInvTestBasic, + TestParameterListRemoteMovable); +INSTANTIATE_TYPED_TEST_SUITE_P(RemoteNonMovable, AnyInvTestBasic, + TestParameterListRemoteNonMovable); + +INSTANTIATE_TYPED_TEST_SUITE_P(Local, AnyInvTestBasic, TestParameterListLocal); + +INSTANTIATE_TYPED_TEST_SUITE_P(NonRvalueCallNothrow, AnyInvTestBasic, + TestParameterListNonRvalueQualifiersNothrowCall); +INSTANTIATE_TYPED_TEST_SUITE_P(CallNothrowRvalue, AnyInvTestBasic, + TestParameterListRvalueQualifiersNothrowCall); + +// 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); + +INSTANTIATE_TYPED_TEST_SUITE_P( + NonRvalueCallMayThrow, AnyInvTestCombinatoric, + TestParameterListNonRvalueQualifiersCallMayThrow); +INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallMayThrow, AnyInvTestCombinatoric, + TestParameterListRvalueQualifiersCallMayThrow); + +INSTANTIATE_TYPED_TEST_SUITE_P(RemoteMovable, AnyInvTestCombinatoric, + TestParameterListRemoteMovable); +INSTANTIATE_TYPED_TEST_SUITE_P(RemoteNonMovable, AnyInvTestCombinatoric, + TestParameterListRemoteNonMovable); + +INSTANTIATE_TYPED_TEST_SUITE_P(Local, AnyInvTestCombinatoric, + TestParameterListLocal); + +INSTANTIATE_TYPED_TEST_SUITE_P(NonRvalueCallNothrow, AnyInvTestCombinatoric, + TestParameterListNonRvalueQualifiersNothrowCall); +INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallNothrow, AnyInvTestCombinatoric, + TestParameterListRvalueQualifiersNothrowCall); + +REGISTER_TYPED_TEST_SUITE_P(AnyInvTestMovable, + ConversionConstructionUserDefinedType, + ConversionConstructionVoidCovariance, + ConversionAssignUserDefinedTypeEmptyLhs, + ConversionAssignUserDefinedTypeNonemptyLhs, + ConversionAssignVoidCovariance); + +INSTANTIATE_TYPED_TEST_SUITE_P( + NonRvalueCallMayThrow, AnyInvTestMovable, + TestParameterListNonRvalueQualifiersCallMayThrow); +INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallMayThrow, AnyInvTestMovable, + TestParameterListRvalueQualifiersCallMayThrow); + +INSTANTIATE_TYPED_TEST_SUITE_P(RemoteMovable, AnyInvTestMovable, + TestParameterListRemoteMovable); + +INSTANTIATE_TYPED_TEST_SUITE_P(Local, AnyInvTestMovable, + TestParameterListLocal); + +INSTANTIATE_TYPED_TEST_SUITE_P(NonRvalueCallNothrow, AnyInvTestMovable, + TestParameterListNonRvalueQualifiersNothrowCall); +INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallNothrow, AnyInvTestMovable, + TestParameterListRvalueQualifiersNothrowCall); + +REGISTER_TYPED_TEST_SUITE_P(AnyInvTestNoexceptFalse, + ConversionConstructionConstraints, + ConversionAssignConstraints); + +INSTANTIATE_TYPED_TEST_SUITE_P( + NonRvalueCallMayThrow, AnyInvTestNoexceptFalse, + TestParameterListNonRvalueQualifiersCallMayThrow); +INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallMayThrow, AnyInvTestNoexceptFalse, + TestParameterListRvalueQualifiersCallMayThrow); + +INSTANTIATE_TYPED_TEST_SUITE_P(RemoteMovable, AnyInvTestNoexceptFalse, + TestParameterListRemoteMovable); +INSTANTIATE_TYPED_TEST_SUITE_P(RemoteNonMovable, AnyInvTestNoexceptFalse, + TestParameterListRemoteNonMovable); + +INSTANTIATE_TYPED_TEST_SUITE_P(Local, AnyInvTestNoexceptFalse, + TestParameterListLocal); + +REGISTER_TYPED_TEST_SUITE_P(AnyInvTestNoexceptTrue, + ConversionConstructionConstraints, + ConversionAssignConstraints); + +INSTANTIATE_TYPED_TEST_SUITE_P(NonRvalueCallNothrow, AnyInvTestNoexceptTrue, + TestParameterListNonRvalueQualifiersNothrowCall); +INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallNothrow, AnyInvTestNoexceptTrue, + TestParameterListRvalueQualifiersNothrowCall); + +REGISTER_TYPED_TEST_SUITE_P(AnyInvTestNonRvalue, + ConversionConstructionReferenceWrapper, + NonMoveableResultType, + ConversionAssignReferenceWrapperEmptyLhs, + ConversionAssignReferenceWrapperNonemptyLhs); + +INSTANTIATE_TYPED_TEST_SUITE_P( + NonRvalueCallMayThrow, AnyInvTestNonRvalue, + TestParameterListNonRvalueQualifiersCallMayThrow); + +INSTANTIATE_TYPED_TEST_SUITE_P(RemoteMovable, AnyInvTestNonRvalue, + TestParameterListRemoteMovable); +INSTANTIATE_TYPED_TEST_SUITE_P(RemoteNonMovable, AnyInvTestNonRvalue, + TestParameterListRemoteNonMovable); + +INSTANTIATE_TYPED_TEST_SUITE_P(Local, AnyInvTestNonRvalue, + TestParameterListLocal); + +INSTANTIATE_TYPED_TEST_SUITE_P(NonRvalueCallNothrow, AnyInvTestNonRvalue, + TestParameterListNonRvalueQualifiersNothrowCall); + +REGISTER_TYPED_TEST_SUITE_P(AnyInvTestRvalue, + ConversionConstructionReferenceWrapper, + NonMoveableResultType, + ConversionAssignReferenceWrapper); + +INSTANTIATE_TYPED_TEST_SUITE_P(RvalueCallMayThrow, AnyInvTestRvalue, + TestParameterListRvalueQualifiersCallMayThrow); + +INSTANTIATE_TYPED_TEST_SUITE_P(CallNothrowRvalue, AnyInvTestRvalue, + TestParameterListRvalueQualifiersNothrowCall); + +// Minimal SFINAE testing for platforms where we can't run the tests, but we can +// build binaries for. +static_assert( + std::is_convertible<void (*)(), absl::AnyInvocable<void() &&>>::value, ""); +static_assert(!std::is_convertible<void*, absl::AnyInvocable<void() &&>>::value, + ""); + +#undef ABSL_INTERNAL_NOEXCEPT_SPEC + +} // namespace |