From d90a748ec0c1552377e85a0e6d15ee6a5435668a Mon Sep 17 00:00:00 2001 From: Abseil Team <absl-team@google.com> Date: Thu, 10 Oct 2019 13:12:54 -0400 Subject: [PATCH] Googletest export Fix the O(n^2) number of instantiations in ElemFromList. It is now O(n). It still has O(1) instantiation depth. PiperOrigin-RevId: 273980821 --- .../gmock/internal/gmock-internal-utils.h | 3 +- .../include/gtest/internal/gtest-internal.h | 44 ++++++++++--------- googletest/test/gtest_unittest.cc | 21 ++++----- 3 files changed, 32 insertions(+), 36 deletions(-) diff --git a/googlemock/include/gmock/internal/gmock-internal-utils.h b/googlemock/include/gmock/internal/gmock-internal-utils.h index d012e710..584afa98 100644 --- a/googlemock/include/gmock/internal/gmock-internal-utils.h +++ b/googlemock/include/gmock/internal/gmock-internal-utils.h @@ -490,8 +490,7 @@ struct Function<R(Args...)> { using Result = R; static constexpr size_t ArgumentCount = sizeof...(Args); template <size_t I> - using Arg = ElemFromList<I, typename MakeIndexSequence<sizeof...(Args)>::type, - Args...>; + using Arg = ElemFromList<I, Args...>; using ArgumentTuple = std::tuple<Args...>; using ArgumentMatcherTuple = std::tuple<Matcher<Args>...>; using MakeResultVoid = void(Args...); diff --git a/googletest/include/gtest/internal/gtest-internal.h b/googletest/include/gtest/internal/gtest-internal.h index 76ce13ae..ebfe3c9d 100644 --- a/googletest/include/gtest/internal/gtest-internal.h +++ b/googletest/include/gtest/internal/gtest-internal.h @@ -1124,25 +1124,29 @@ struct MakeIndexSequence template <> struct MakeIndexSequence<0> : IndexSequence<> {}; -// FIXME: This implementation of ElemFromList is O(1) in instantiation depth, -// but it is O(N^2) in total instantiations. Not sure if this is the best -// tradeoff, as it will make it somewhat slow to compile. -template <typename T, size_t, size_t> -struct ElemFromListImpl {}; - -template <typename T, size_t I> -struct ElemFromListImpl<T, I, I> { - using type = T; +template <size_t> +struct Ignore { + Ignore(...); // NOLINT }; -// Get the Nth element from T... -// It uses O(1) instantiation depth. -template <size_t N, typename I, typename... T> -struct ElemFromList; +template <typename> +struct ElemFromListImpl; +template <size_t... I> +struct ElemFromListImpl<IndexSequence<I...>> { + // We make Ignore a template to solve a problem with MSVC. + // A non-template Ignore would work fine with `decltype(Ignore(I))...`, but + // MSVC doesn't understand how to deal with that pack expansion. + // Use `0 * I` to have a single instantiation of Ignore. + template <typename R> + static R Apply(Ignore<0 * I>..., R (*)(), ...); +}; -template <size_t N, size_t... I, typename... T> -struct ElemFromList<N, IndexSequence<I...>, T...> - : ElemFromListImpl<T, N, I>... {}; +template <size_t N, typename... T> +struct ElemFromList { + using type = + decltype(ElemFromListImpl<typename MakeIndexSequence<N>::type>::Apply( + static_cast<T (*)()>(nullptr)...)); +}; template <typename... T> class FlatTuple; @@ -1152,9 +1156,7 @@ struct FlatTupleElemBase; template <typename... T, size_t I> struct FlatTupleElemBase<FlatTuple<T...>, I> { - using value_type = - typename ElemFromList<I, typename MakeIndexSequence<sizeof...(T)>::type, - T...>::type; + using value_type = typename ElemFromList<I, T...>::type; FlatTupleElemBase() = default; explicit FlatTupleElemBase(value_type t) : value(std::move(t)) {} value_type value; @@ -1192,12 +1194,12 @@ class FlatTuple explicit FlatTuple(T... t) : FlatTuple::FlatTupleBase(std::move(t)...) {} template <size_t I> - const typename ElemFromList<I, Indices, T...>::type& Get() const { + const typename ElemFromList<I, T...>::type& Get() const { return static_cast<const FlatTupleElemBase<FlatTuple, I>*>(this)->value; } template <size_t I> - typename ElemFromList<I, Indices, T...>::type& Get() { + typename ElemFromList<I, T...>::type& Get() { return static_cast<FlatTupleElemBase<FlatTuple, I>*>(this)->value; } }; diff --git a/googletest/test/gtest_unittest.cc b/googletest/test/gtest_unittest.cc index 05ee1c70..8312bd10 100644 --- a/googletest/test/gtest_unittest.cc +++ b/googletest/test/gtest_unittest.cc @@ -7353,20 +7353,15 @@ TEST(IndexSequence, MakeIndexSequence) { // ElemFromList TEST(ElemFromList, Basic) { using testing::internal::ElemFromList; - using Idx = testing::internal::MakeIndexSequence<3>::type; + EXPECT_TRUE( + (std::is_same<int, ElemFromList<0, int, double, char>::type>::value)); + EXPECT_TRUE( + (std::is_same<double, ElemFromList<1, int, double, char>::type>::value)); + EXPECT_TRUE( + (std::is_same<char, ElemFromList<2, int, double, char>::type>::value)); EXPECT_TRUE(( - std::is_same<int, ElemFromList<0, Idx, int, double, char>::type>::value)); - EXPECT_TRUE( - (std::is_same<double, - ElemFromList<1, Idx, int, double, char>::type>::value)); - EXPECT_TRUE( - (std::is_same<char, - ElemFromList<2, Idx, int, double, char>::type>::value)); - EXPECT_TRUE( - (std::is_same< - char, ElemFromList<7, testing::internal::MakeIndexSequence<12>::type, - int, int, int, int, int, int, int, char, int, int, - int, int>::type>::value)); + std::is_same<char, ElemFromList<7, int, int, int, int, int, int, int, + char, int, int, int, int>::type>::value)); } // FlatTuple