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 ++++++ absl/functional/bind_front_test.cc | 19 +++ absl/status/status.cc | 2 - absl/strings/str_split.h | 1 + .../internal/cctz/include/cctz/civil_time_detail.h | 42 ++++--- absl/time/internal/cctz/src/civil_time_test.cc | 10 ++ absl/types/span.h | 15 +-- 8 files changed, 194 insertions(+), 60 deletions(-) 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())); } diff --git a/absl/functional/bind_front_test.cc b/absl/functional/bind_front_test.cc index 4801a81c..92b6e8e5 100644 --- a/absl/functional/bind_front_test.cc +++ b/absl/functional/bind_front_test.cc @@ -228,4 +228,23 @@ TEST(BindTest, Mangling) { absl::bind_front(ManglingCall{}, 1, 3.3)("A"); } +struct Adder { + int add(int v2) const { return v + v2; } + int v; +}; + +TEST(BindTest, InvokeSemantics) { + Struct s1 = {"value"}; + auto f1 = absl::bind_front(&Struct::value); + EXPECT_EQ(f1(s1), "value"); + EXPECT_EQ(f1(&s1), "value"); + EXPECT_EQ(f1(std::ref(s1)), "value"); + + Adder add_100 = {100}; + auto f2 = absl::bind_front(&Adder::add); + EXPECT_EQ(f2(add_100, 23), 123); + EXPECT_EQ(f2(&add_100, 45), 145); + EXPECT_EQ(f2(std::ref(add_100), 67), 167); +} + } // namespace diff --git a/absl/status/status.cc b/absl/status/status.cc index 6d57a6be..0a655736 100644 --- a/absl/status/status.cc +++ b/absl/status/status.cc @@ -27,8 +27,6 @@ namespace absl { ABSL_NAMESPACE_BEGIN -// The implementation was intentionally kept same as util::error::Code_Name() -// to ease the migration. std::string StatusCodeToString(StatusCode code) { switch (code) { case StatusCode::kOk: diff --git a/absl/strings/str_split.h b/absl/strings/str_split.h index a79cd4a0..1ce17f38 100644 --- a/absl/strings/str_split.h +++ b/absl/strings/str_split.h @@ -44,6 +44,7 @@ #include #include "absl/base/internal/raw_logging.h" +#include "absl/base/macros.h" #include "absl/strings/internal/str_split_internal.h" #include "absl/strings/string_view.h" #include "absl/strings/strip.h" diff --git a/absl/time/internal/cctz/include/cctz/civil_time_detail.h b/absl/time/internal/cctz/include/cctz/civil_time_detail.h index 4cde96f1..d1b4222b 100644 --- a/absl/time/internal/cctz/include/cctz/civil_time_detail.h +++ b/absl/time/internal/cctz/include/cctz/civil_time_detail.h @@ -106,54 +106,64 @@ CONSTEXPR_F int days_per_month(year_t y, month_t m) noexcept { CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd, hour_t hh, minute_t mm, second_t ss) noexcept { - y += (cd / 146097) * 400; + year_t ey = y % 400; + const year_t oey = ey; + ey += (cd / 146097) * 400; cd %= 146097; if (cd < 0) { - y -= 400; + ey -= 400; cd += 146097; } - y += (d / 146097) * 400; + ey += (d / 146097) * 400; d = d % 146097 + cd; if (d > 0) { if (d > 146097) { - y += 400; + ey += 400; d -= 146097; } } else { if (d > -365) { // We often hit the previous year when stepping a civil time backwards, // so special case it to avoid counting up by 100/4/1-year chunks. - y -= 1; - d += days_per_year(y, m); + ey -= 1; + d += days_per_year(ey, m); } else { - y -= 400; + ey -= 400; d += 146097; } } if (d > 365) { - for (int n = days_per_century(y, m); d > n; n = days_per_century(y, m)) { + for (;;) { + int n = days_per_century(ey, m); + if (d <= n) break; d -= n; - y += 100; + ey += 100; } - for (int n = days_per_4years(y, m); d > n; n = days_per_4years(y, m)) { + for (;;) { + int n = days_per_4years(ey, m); + if (d <= n) break; d -= n; - y += 4; + ey += 4; } - for (int n = days_per_year(y, m); d > n; n = days_per_year(y, m)) { + for (;;) { + int n = days_per_year(ey, m); + if (d <= n) break; d -= n; - ++y; + ++ey; } } if (d > 28) { - for (int n = days_per_month(y, m); d > n; n = days_per_month(y, m)) { + for (;;) { + int n = days_per_month(ey, m); + if (d <= n) break; d -= n; if (++m > 12) { - ++y; + ++ey; m = 1; } } } - return fields(y, m, static_cast(d), hh, mm, ss); + return fields(y + (ey - oey), m, static_cast(d), hh, mm, ss); } CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd, hour_t hh, minute_t mm, second_t ss) noexcept { diff --git a/absl/time/internal/cctz/src/civil_time_test.cc b/absl/time/internal/cctz/src/civil_time_test.cc index be894d70..a5a71230 100644 --- a/absl/time/internal/cctz/src/civil_time_test.cc +++ b/absl/time/internal/cctz/src/civil_time_test.cc @@ -234,6 +234,16 @@ TEST(CivilTime, Difference) { static_assert(diff == 365, "Difference"); } +// NOTE: Run this with --copt=-ftrapv to detect overflow problems. +TEST(CivilTime, ConstructionWithHugeYear) { + constexpr civil_hour h(-9223372036854775807, 1, 1, -1); + static_assert(h.year() == -9223372036854775807 - 1, + "ConstructionWithHugeYear"); + static_assert(h.month() == 12, "ConstructionWithHugeYear"); + static_assert(h.day() == 31, "ConstructionWithHugeYear"); + static_assert(h.hour() == 23, "ConstructionWithHugeYear"); +} + // NOTE: Run this with --copt=-ftrapv to detect overflow problems. TEST(CivilTime, DifferenceWithHugeYear) { { diff --git a/absl/types/span.h b/absl/types/span.h index 734db695..4e450fc9 100644 --- a/absl/types/span.h +++ b/absl/types/span.h @@ -27,22 +27,17 @@ // `Span` when such types may be difficult to identify due to issues // with implicit conversion. // -// The C++ standards committee currently has a proposal for a `std::span` type, -// (http://wg21.link/p0122), which is not yet part of the standard (though may -// become part of C++20). As of August 2017, the differences between -// `absl::Span` and this proposal are: -// * `absl::Span` uses `size_t` for `size_type` -// * `absl::Span` has no `operator()` -// * `absl::Span` has no constructors for `std::unique_ptr` or -// `std::shared_ptr` +// The C++20 draft standard includes a `std::span` type. As of June 2020, the +// differences between `absl::Span` and `std::span` are: +// * `absl::Span` has `operator==` (which is likely a design bug, +// per https://abseil.io/blog/20180531-regular-types) // * `absl::Span` has the factory functions `MakeSpan()` and // `MakeConstSpan()` -// * `absl::Span` has `front()` and `back()` methods // * bounds-checked access to `absl::Span` is accomplished with `at()` // * `absl::Span` has compiler-provided move and copy constructors and // assignment. This is due to them being specified as `constexpr`, but that // implies const in C++11. -// * `absl::Span` has no `element_type` or `index_type` typedefs +// * `absl::Span` has no `element_type` typedef // * A read-only `absl::Span` can be implicitly constructed from an // initializer list. // * `absl::Span` has no `bytes()`, `size_bytes()`, `as_bytes()`, or -- cgit v1.2.3