From e7ebf9803746b9a115d96164bdf5e915be8f223b Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Wed, 10 Jun 2020 15:35:34 -0700 Subject: Export of internal Abseil changes -- 44b312da54263fc7491b5f1e115e49e0c1e2dc10 by Greg Falcon : Import of CCTZ from GitHub. PiperOrigin-RevId: 315782632 -- 27618a3b195f75384ba44e9712ae0b0b7d85937e by Greg Falcon : Update Abseil's internal Invoke() implementation to follow C++17 semantics. Starting in C++17, when invoke'ing a pointer-to-member, if the object representing the class is a reference_wrapper, that wrapper is unpacked. Because we implement a number of functional APIs that closely match C++ standard proposals, it is better if we follow the standard's notion of what "invoking" means. This also makes `absl::base_internal::Invoke()` match `std::invoke()` in C++17. I intend to make this an alias in a follow-up CL. PiperOrigin-RevId: 315750659 -- 059519ea402cd55b1b716403bb680504c6ff5808 by Xiaoyi Zhang : Internal change PiperOrigin-RevId: 315597064 -- 5e2042c8520576b2508e2bfb1020a97c7db591da by Titus Winters : Update notes on the delta between absl::Span vs. std::span. PiperOrigin-RevId: 315518942 -- 9d3875527b93124f5de5d6a1d575c42199fbf323 by Abseil Team : Internal cleanup PiperOrigin-RevId: 315497633 GitOrigin-RevId: 44b312da54263fc7491b5f1e115e49e0c1e2dc10 Change-Id: I24573f317c8388bd693c0fdab395a7dd419b33b0 --- absl/base/internal/invoke.h | 131 +++++++++++++++++++++++++++++++++----------- absl/base/invoke_test.cc | 34 ++++++++++++ 2 files changed, 133 insertions(+), 32 deletions(-) (limited to 'absl/base') diff --git a/absl/base/internal/invoke.h b/absl/base/internal/invoke.h index c4eceebd..ab5ad910 100644 --- a/absl/base/internal/invoke.h +++ b/absl/base/internal/invoke.h @@ -18,16 +18,19 @@ // [func.require] // Define INVOKE (f, t1, t2, ..., tN) as follows: // 1. (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T -// and t1 is an object of type T or a reference to an object of type T or a -// reference to an object of a type derived from T; -// 2. ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a -// class T and t1 is not one of the types described in the previous item; -// 3. t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is -// an object of type T or a reference to an object of type T or a reference -// to an object of a type derived from T; -// 4. (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 -// is not one of the types described in the previous item; -// 5. f(t1, t2, ..., tN) in all other cases. +// and is_base_of_v> is true; +// 2. (t1.get().*f)(t2, ..., tN) when f is a pointer to a member function of a +// class T and remove_cvref_t is a specialization of +// reference_wrapper; +// 3. ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a +// class T and t1 does not satisfy the previous two items; +// 4. t1.*f when N == 1 and f is a pointer to data member of a class T and +// is_base_of_v> is true; +// 5. t1.get().*f when N == 1 and f is a pointer to data member of a class T and +// remove_cvref_t is a specialization of reference_wrapper; +// 6. (*t1).*f when N == 1 and f is a pointer to data member of a class T and t1 +// does not satisfy the previous two items; +// 7. f(t1, t2, ..., tN) in all other cases. // // The implementation is SFINAE-friendly: substitution failure within Invoke() // isn't an error. @@ -48,7 +51,16 @@ namespace absl { ABSL_NAMESPACE_BEGIN namespace base_internal { -// The five classes below each implement one of the clauses from the definition +template +struct IsReferenceWrapper : std::false_type {}; +template +struct IsReferenceWrapper> : std::true_type {}; + +template +using RemoveCvrefT = + typename std::remove_cv::type>::type; + +// The seven classes below each implement one of the clauses from the definition // of INVOKE. The inner class template Accept checks whether the // clause is applicable; static function template Invoke(f, args...) does the // invocation. @@ -72,9 +84,10 @@ struct MemFunAndRef : StrippedAccept { template struct AcceptImpl - : std::integral_constant::value && - absl::is_function::value> { - }; + : std::integral_constant< + bool, std::is_base_of< + C, typename std::remove_reference::type>::value && + absl::is_function::value> {}; template static decltype((std::declval().* @@ -85,17 +98,41 @@ struct MemFunAndRef : StrippedAccept { } }; +// (t1.get().*f)(t2, ..., tN) when f is a pointer to a member function of a +// class T and remove_cvref_t is a specialization of +// reference_wrapper; +struct MemFunAndRefWrap : StrippedAccept { + template + struct AcceptImpl : std::false_type {}; + + template + struct AcceptImpl + : std::integral_constant< + bool, IsReferenceWrapper>::value && + absl::is_function::value> {}; + + template + static decltype((std::declval().get().* + std::declval())(std::declval()...)) + Invoke(MemFun&& mem_fun, RefWrap&& ref_wrap, Args&&... args) { + return (std::forward(ref_wrap).get().* + std::forward(mem_fun))(std::forward(args)...); + } +}; + // ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a -// class T and t1 is not one of the types described in the previous item. +// class T and t1 does not satisfy the previous two items; struct MemFunAndPtr : StrippedAccept { template struct AcceptImpl : std::false_type {}; template struct AcceptImpl - : std::integral_constant::value && - absl::is_function::value> { - }; + : std::integral_constant< + bool, !std::is_base_of< + C, typename std::remove_reference::type>::value && + !IsReferenceWrapper>::value && + absl::is_function::value> {}; template static decltype(((*std::declval()).* @@ -106,17 +143,18 @@ struct MemFunAndPtr : StrippedAccept { } }; -// t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is -// an object of type T or a reference to an object of type T or a reference -// to an object of a type derived from T. +// t1.*f when N == 1 and f is a pointer to data member of a class T and +// is_base_of_v> is true; struct DataMemAndRef : StrippedAccept { template struct AcceptImpl : std::false_type {}; template struct AcceptImpl - : std::integral_constant::value && - !absl::is_function::value> {}; + : std::integral_constant< + bool, std::is_base_of< + C, typename std::remove_reference::type>::value && + !absl::is_function::value> {}; template static decltype(std::declval().*std::declval()) Invoke( @@ -125,16 +163,39 @@ struct DataMemAndRef : StrippedAccept { } }; -// (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 -// is not one of the types described in the previous item. +// t1.get().*f when N == 1 and f is a pointer to data member of a class T and +// remove_cvref_t is a specialization of reference_wrapper; +struct DataMemAndRefWrap : StrippedAccept { + template + struct AcceptImpl : std::false_type {}; + + template + struct AcceptImpl + : std::integral_constant< + bool, IsReferenceWrapper>::value && + !absl::is_function::value> {}; + + template + static decltype(std::declval().get().*std::declval()) + Invoke(DataMem&& data_mem, RefWrap&& ref_wrap) { + return std::forward(ref_wrap).get().* + std::forward(data_mem); + } +}; + +// (*t1).*f when N == 1 and f is a pointer to data member of a class T and t1 +// does not satisfy the previous two items; struct DataMemAndPtr : StrippedAccept { template struct AcceptImpl : std::false_type {}; template struct AcceptImpl - : std::integral_constant::value && - !absl::is_function::value> {}; + : std::integral_constant< + bool, !std::is_base_of< + C, typename std::remove_reference::type>::value && + !IsReferenceWrapper>::value && + !absl::is_function::value> {}; template static decltype((*std::declval()).*std::declval()) Invoke( @@ -160,12 +221,18 @@ struct Invoker { typedef typename std::conditional< MemFunAndRef::Accept::value, MemFunAndRef, typename std::conditional< - MemFunAndPtr::Accept::value, MemFunAndPtr, + MemFunAndRefWrap::Accept::value, MemFunAndRefWrap, typename std::conditional< - DataMemAndRef::Accept::value, DataMemAndRef, - typename std::conditional::value, - DataMemAndPtr, Callable>::type>::type>:: - type>::type type; + MemFunAndPtr::Accept::value, MemFunAndPtr, + typename std::conditional< + DataMemAndRef::Accept::value, DataMemAndRef, + typename std::conditional< + DataMemAndRefWrap::Accept::value, + DataMemAndRefWrap, + typename std::conditional< + DataMemAndPtr::Accept::value, DataMemAndPtr, + Callable>::type>::type>::type>::type>::type>::type + type; }; // The result type of Invoke. diff --git a/absl/base/invoke_test.cc b/absl/base/invoke_test.cc index 6aa613c9..994d36b9 100644 --- a/absl/base/invoke_test.cc +++ b/absl/base/invoke_test.cc @@ -158,31 +158,56 @@ TEST(InvokeTest, MemberFunction) { std::unique_ptr cp(new Class); std::unique_ptr vp(new Class); + Class c; + std::reference_wrapper ref(c); + std::reference_wrapper ref_const(c); + const std::reference_wrapper const_ref(c); + std::reference_wrapper ref_volatile(c); + EXPECT_EQ(1, Invoke(&Class::Method, p, 3, 2)); EXPECT_EQ(1, Invoke(&Class::Method, p.get(), 3, 2)); EXPECT_EQ(1, Invoke(&Class::Method, *p, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::Method, ref, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::Method, const_ref, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::Method, std::move(ref), 3, 2)); EXPECT_EQ(1, Invoke(&Class::RefMethod, p, 3, 2)); EXPECT_EQ(1, Invoke(&Class::RefMethod, p.get(), 3, 2)); EXPECT_EQ(1, Invoke(&Class::RefMethod, *p, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::RefMethod, ref, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::RefMethod, const_ref, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::RefMethod, std::move(ref), 3, 2)); EXPECT_EQ(1, Invoke(&Class::RefRefMethod, std::move(*p), 3, 2)); // NOLINT EXPECT_EQ(1, Invoke(&Class::NoExceptMethod, p, 3, 2)); EXPECT_EQ(1, Invoke(&Class::NoExceptMethod, p.get(), 3, 2)); EXPECT_EQ(1, Invoke(&Class::NoExceptMethod, *p, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::NoExceptMethod, ref, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::NoExceptMethod, const_ref, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::NoExceptMethod, std::move(ref), 3, 2)); EXPECT_EQ(1, Invoke(&Class::ConstMethod, p, 3, 2)); EXPECT_EQ(1, Invoke(&Class::ConstMethod, p.get(), 3, 2)); EXPECT_EQ(1, Invoke(&Class::ConstMethod, *p, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::ConstMethod, ref, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::ConstMethod, const_ref, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::ConstMethod, std::move(ref), 3, 2)); EXPECT_EQ(1, Invoke(&Class::ConstMethod, cp, 3, 2)); EXPECT_EQ(1, Invoke(&Class::ConstMethod, cp.get(), 3, 2)); EXPECT_EQ(1, Invoke(&Class::ConstMethod, *cp, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::ConstMethod, ref_const, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::ConstMethod, std::move(ref_const), 3, 2)); EXPECT_EQ(1, Invoke(&Class::VolatileMethod, p, 3, 2)); EXPECT_EQ(1, Invoke(&Class::VolatileMethod, p.get(), 3, 2)); EXPECT_EQ(1, Invoke(&Class::VolatileMethod, *p, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::VolatileMethod, ref, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::VolatileMethod, const_ref, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::VolatileMethod, std::move(ref), 3, 2)); EXPECT_EQ(1, Invoke(&Class::VolatileMethod, vp, 3, 2)); EXPECT_EQ(1, Invoke(&Class::VolatileMethod, vp.get(), 3, 2)); EXPECT_EQ(1, Invoke(&Class::VolatileMethod, *vp, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::VolatileMethod, ref_volatile, 3, 2)); + EXPECT_EQ(1, Invoke(&Class::VolatileMethod, std::move(ref_volatile), 3, 2)); EXPECT_EQ(1, Invoke(&Class::Method, make_unique(), 3, 2)); EXPECT_EQ(1, Invoke(&Class::ConstMethod, make_unique(), 3, 2)); @@ -192,8 +217,15 @@ TEST(InvokeTest, MemberFunction) { TEST(InvokeTest, DataMember) { std::unique_ptr p(new Class{42}); std::unique_ptr cp(new Class{42}); + Class c{42}; + std::reference_wrapper ref(c); + std::reference_wrapper ref_const(c); + const std::reference_wrapper const_ref(c); EXPECT_EQ(42, Invoke(&Class::member, p)); EXPECT_EQ(42, Invoke(&Class::member, *p)); + EXPECT_EQ(42, Invoke(&Class::member, ref)); + EXPECT_EQ(42, Invoke(&Class::member, const_ref)); + EXPECT_EQ(42, Invoke(&Class::member, std::move(ref))); EXPECT_EQ(42, Invoke(&Class::member, p.get())); Invoke(&Class::member, p) = 42; @@ -201,6 +233,8 @@ TEST(InvokeTest, DataMember) { EXPECT_EQ(42, Invoke(&Class::member, cp)); EXPECT_EQ(42, Invoke(&Class::member, *cp)); + EXPECT_EQ(42, Invoke(&Class::member, ref_const)); + EXPECT_EQ(42, Invoke(&Class::member, std::move(ref_const))); EXPECT_EQ(42, Invoke(&Class::member, cp.get())); } -- cgit v1.2.3