Googletest export

Change ValuesArray to require much less template instantiation depth.

PiperOrigin-RevId: 218170842
This commit is contained in:
Abseil Team 2018-10-22 11:21:18 -04:00 committed by Gennadiy Civil
parent 32dbcac06e
commit 82987067d8
3 changed files with 193 additions and 37 deletions

View File

@ -1176,6 +1176,112 @@ class NativeArray {
GTEST_DISALLOW_ASSIGN_(NativeArray); GTEST_DISALLOW_ASSIGN_(NativeArray);
}; };
// Backport of std::index_sequence.
template <size_t... Is>
struct IndexSequence {
using type = IndexSequence;
};
// Double the IndexSequence, and one if plus_one is true.
template <bool plus_one, typename T, size_t sizeofT>
struct DoubleSequence;
template <size_t... I, size_t sizeofT>
struct DoubleSequence<true, IndexSequence<I...>, sizeofT> {
using type = IndexSequence<I..., (sizeofT + I)..., 2 * sizeofT>;
};
template <size_t... I, size_t sizeofT>
struct DoubleSequence<false, IndexSequence<I...>, sizeofT> {
using type = IndexSequence<I..., (sizeofT + I)...>;
};
// Backport of std::make_index_sequence.
// It uses O(ln(N)) instantiation depth.
template <size_t N>
struct MakeIndexSequence
: DoubleSequence<N % 2 == 1, typename MakeIndexSequence<N / 2>::type,
N / 2>::type {};
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;
};
// Get the Nth element from T...
// It uses O(1) instantiation depth.
template <size_t N, typename I, typename... T>
struct ElemFromList;
template <size_t N, size_t... I, typename... T>
struct ElemFromList<N, IndexSequence<I...>, T...>
: ElemFromListImpl<T, N, I>... {};
template <typename... T>
class FlatTuple;
template <typename Derived, size_t I>
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;
FlatTupleElemBase() = default;
explicit FlatTupleElemBase(value_type t) : value(std::move(t)) {}
value_type value;
};
template <typename Derived, typename Idx>
struct FlatTupleBase;
template <size_t... Idx, typename... T>
struct FlatTupleBase<FlatTuple<T...>, IndexSequence<Idx...>>
: FlatTupleElemBase<FlatTuple<T...>, Idx>... {
using Indices = IndexSequence<Idx...>;
FlatTupleBase() = default;
explicit FlatTupleBase(T... t)
: FlatTupleElemBase<FlatTuple<T...>, Idx>(std::move(t))... {}
};
// Analog to std::tuple but with different tradeoffs.
// This class minimizes the template instantiation depth, thus allowing more
// elements that std::tuple would. std::tuple has been seen to require an
// instantiation depth of more than 10x the number of elements in some
// implementations.
// FlatTuple and ElemFromList are not recursive and have a fixed depth
// regardless of T...
// MakeIndexSequence, on the other hand, it is recursive but with an
// instantiation depth of O(ln(N)).
template <typename... T>
class FlatTuple
: private FlatTupleBase<FlatTuple<T...>,
typename MakeIndexSequence<sizeof...(T)>::type> {
using Indices = typename FlatTuple::FlatTupleBase::Indices;
public:
FlatTuple() = default;
explicit FlatTuple(T... t) : FlatTuple::FlatTupleBase(std::move(t)...) {}
template <size_t I>
const typename ElemFromList<I, Indices, T...>::type& Get() const {
return static_cast<const FlatTupleElemBase<FlatTuple, I>*>(this)->value;
}
template <size_t I>
typename ElemFromList<I, Indices, T...>::type& Get() {
return static_cast<FlatTupleElemBase<FlatTuple, I>*>(this)->value;
}
};
} // namespace internal } // namespace internal
} // namespace testing } // namespace testing

View File

@ -74,27 +74,6 @@ namespace internal {
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
// Utility Functions // Utility Functions
// Block of code creating for_each_in_tuple
template <int... Is>
struct sequence {};
template <int N, int... Is>
struct generate_sequence : generate_sequence<N - 1, N - 1, Is...> {};
template <int... Is>
struct generate_sequence<0, Is...> : sequence<Is...> {};
template <typename T, typename F, int... Is>
void ForEachInTupleImpl(T&& t, F f_gtest, sequence<Is...>) {
int l[] = {(f_gtest(std::get<Is>(t)), 0)...};
(void)l; // silence "unused variable warning"
}
template <typename... T, typename F>
void ForEachInTuple(const std::tuple<T...>& t, F f_gtest) {
internal::ForEachInTupleImpl(t, f_gtest,
internal::generate_sequence<sizeof...(T)>());
}
// Outputs a message explaining invalid registration of different // Outputs a message explaining invalid registration of different
// fixture class for the same test case. This may happen when // fixture class for the same test case. This may happen when
// TEST_P macro is used to define two tests with the same name // TEST_P macro is used to define two tests with the same name
@ -747,30 +726,23 @@ internal::ParamGenerator<typename Container::value_type> ValuesIn(
namespace internal { namespace internal {
// Used in the Values() function to provide polymorphic capabilities. // Used in the Values() function to provide polymorphic capabilities.
template <typename T>
struct PushBack {
template <typename U>
void operator()(const U& u) {
v_.push_back(static_cast<T>(u));
}
std::vector<T>& v_;
};
template <typename... Ts> template <typename... Ts>
class ValueArray { class ValueArray {
public: public:
ValueArray(Ts... v) : v_{std::move(v)...} {} ValueArray(Ts... v) : v_{std::move(v)...} {}
template <typename Tn> template <typename T>
operator ParamGenerator<Tn>() const { operator ParamGenerator<T>() const { // NOLINT
std::vector<Tn> vc_accumulate; return ValuesIn(MakeVector<T>(MakeIndexSequence<sizeof...(Ts)>()));
PushBack<Tn> fnc{vc_accumulate};
ForEachInTuple(v_, fnc);
return ValuesIn(std::move(vc_accumulate));
} }
private: private:
std::tuple<Ts...> v_; template <typename T, size_t... I>
std::vector<T> MakeVector(IndexSequence<I...>) const {
return std::vector<T>{static_cast<T>(v_.template Get<I>())...};
}
FlatTuple<Ts...> v_;
}; };
} // namespace internal } // namespace internal

View File

@ -7450,6 +7450,84 @@ TEST(NativeArrayTest, WorksForTwoDimensionalArray) {
EXPECT_EQ(a, na.begin()); EXPECT_EQ(a, na.begin());
} }
// IndexSequence
TEST(IndexSequence, MakeIndexSequence) {
using testing::internal::IndexSequence;
using testing::internal::MakeIndexSequence;
EXPECT_TRUE(
(std::is_same<IndexSequence<>, MakeIndexSequence<0>::type>::value));
EXPECT_TRUE(
(std::is_same<IndexSequence<0>, MakeIndexSequence<1>::type>::value));
EXPECT_TRUE(
(std::is_same<IndexSequence<0, 1>, MakeIndexSequence<2>::type>::value));
EXPECT_TRUE((
std::is_same<IndexSequence<0, 1, 2>, MakeIndexSequence<3>::type>::value));
EXPECT_TRUE(
(std::is_base_of<IndexSequence<0, 1, 2>, MakeIndexSequence<3>>::value));
}
// ElemFromList
TEST(ElemFromList, Basic) {
using testing::internal::ElemFromList;
using Idx = testing::internal::MakeIndexSequence<3>::type;
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));
}
// FlatTuple
TEST(FlatTuple, Basic) {
using testing::internal::FlatTuple;
FlatTuple<int, double, const char*> tuple = {};
EXPECT_EQ(0, tuple.Get<0>());
EXPECT_EQ(0.0, tuple.Get<1>());
EXPECT_EQ(nullptr, tuple.Get<2>());
tuple = FlatTuple<int, double, const char*>(7, 3.2, "Foo");
EXPECT_EQ(7, tuple.Get<0>());
EXPECT_EQ(3.2, tuple.Get<1>());
EXPECT_EQ(std::string("Foo"), tuple.Get<2>());
tuple.Get<1>() = 5.1;
EXPECT_EQ(5.1, tuple.Get<1>());
}
TEST(FlatTuple, ManyTypes) {
using testing::internal::FlatTuple;
// Instantiate FlatTuple with 257 ints.
// Tests show that we can do it with thousands of elements, but very long
// compile times makes it unusuitable for this test.
#define GTEST_FLAT_TUPLE_INT8 int, int, int, int, int, int, int, int,
#define GTEST_FLAT_TUPLE_INT16 GTEST_FLAT_TUPLE_INT8 GTEST_FLAT_TUPLE_INT8
#define GTEST_FLAT_TUPLE_INT32 GTEST_FLAT_TUPLE_INT16 GTEST_FLAT_TUPLE_INT16
#define GTEST_FLAT_TUPLE_INT64 GTEST_FLAT_TUPLE_INT32 GTEST_FLAT_TUPLE_INT32
#define GTEST_FLAT_TUPLE_INT128 GTEST_FLAT_TUPLE_INT64 GTEST_FLAT_TUPLE_INT64
#define GTEST_FLAT_TUPLE_INT256 GTEST_FLAT_TUPLE_INT128 GTEST_FLAT_TUPLE_INT128
// Let's make sure that we can have a very long list of types without blowing
// up the template instantiation depth.
FlatTuple<GTEST_FLAT_TUPLE_INT256 int> tuple;
tuple.Get<0>() = 7;
tuple.Get<99>() = 17;
tuple.Get<256>() = 1000;
EXPECT_EQ(7, tuple.Get<0>());
EXPECT_EQ(17, tuple.Get<99>());
EXPECT_EQ(1000, tuple.Get<256>());
}
// Tests SkipPrefix(). // Tests SkipPrefix().
TEST(SkipPrefixTest, SkipsWhenPrefixMatches) { TEST(SkipPrefixTest, SkipsWhenPrefixMatches) {