Define specialization of PrintTo(...) for ::std::tuple.

This commit is contained in:
kosak 2014-01-12 19:59:41 +00:00
parent d3eb97f321
commit ccf8e33bc5
3 changed files with 227 additions and 67 deletions

View File

@ -103,6 +103,10 @@
#include "gtest/internal/gtest-port.h" #include "gtest/internal/gtest-port.h"
#include "gtest/internal/gtest-internal.h" #include "gtest/internal/gtest-internal.h"
#if GTEST_HAS_STD_TUPLE_
# include <tuple>
#endif
namespace testing { namespace testing {
// Definitions in the 'internal' and 'internal2' name spaces are // Definitions in the 'internal' and 'internal2' name spaces are
@ -480,14 +484,16 @@ inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) {
} }
#endif // GTEST_HAS_STD_WSTRING #endif // GTEST_HAS_STD_WSTRING
#if GTEST_HAS_TR1_TUPLE #if GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_
// Overload for ::std::tr1::tuple. Needed for printing function arguments,
// which are packed as tuples.
// Helper function for printing a tuple. T must be instantiated with // Helper function for printing a tuple. T must be instantiated with
// a tuple type. // a tuple type.
template <typename T> template <typename T>
void PrintTupleTo(const T& t, ::std::ostream* os); void PrintTupleTo(const T& t, ::std::ostream* os);
#endif // GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_
#if GTEST_HAS_TR1_TUPLE
// Overload for ::std::tr1::tuple. Needed for printing function arguments,
// which are packed as tuples.
// Overloaded PrintTo() for tuples of various arities. We support // Overloaded PrintTo() for tuples of various arities. We support
// tuples of up-to 10 fields. The following implementation works // tuples of up-to 10 fields. The following implementation works
@ -561,6 +567,13 @@ void PrintTo(
} }
#endif // GTEST_HAS_TR1_TUPLE #endif // GTEST_HAS_TR1_TUPLE
#if GTEST_HAS_STD_TUPLE_
template <typename... Types>
void PrintTo(const ::std::tuple<Types...>& t, ::std::ostream* os) {
PrintTupleTo(t, os);
}
#endif // GTEST_HAS_STD_TUPLE_
// Overload for std::pair. // Overload for std::pair.
template <typename T1, typename T2> template <typename T1, typename T2>
void PrintTo(const ::std::pair<T1, T2>& value, ::std::ostream* os) { void PrintTo(const ::std::pair<T1, T2>& value, ::std::ostream* os) {
@ -756,16 +769,65 @@ void UniversalPrint(const T& value, ::std::ostream* os) {
UniversalPrinter<T1>::Print(value, os); UniversalPrinter<T1>::Print(value, os);
} }
#if GTEST_HAS_TR1_TUPLE
typedef ::std::vector<string> Strings; typedef ::std::vector<string> Strings;
// TuplePolicy<TupleT> must provide:
// - tuple_size
// size of tuple TupleT.
// - get<size_t I>(const TupleT& t)
// static function extracting element I of tuple TupleT.
// - tuple_element<size_t I>::type
// type of element I of tuple TupleT.
template <typename TupleT>
struct TuplePolicy;
#if GTEST_HAS_TR1_TUPLE
template <typename TupleT>
struct TuplePolicy {
typedef TupleT Tuple;
static const size_t tuple_size = ::std::tr1::tuple_size<Tuple>::value;
template <size_t I>
struct tuple_element : ::std::tr1::tuple_element<I, Tuple> {};
template <size_t I>
static typename AddReference<
const typename ::std::tr1::tuple_element<I, Tuple>::type>::type get(
const Tuple& tuple) {
return ::std::tr1::get<I>(tuple);
}
};
template <typename TupleT>
const size_t TuplePolicy<TupleT>::tuple_size;
#endif // GTEST_HAS_TR1_TUPLE
#if GTEST_HAS_STD_TUPLE_
template <typename... Types>
struct TuplePolicy< ::std::tuple<Types...> > {
typedef ::std::tuple<Types...> Tuple;
static const size_t tuple_size = ::std::tuple_size<Tuple>::value;
template <size_t I>
struct tuple_element : ::std::tuple_element<I, Tuple> {};
template <size_t I>
static const typename ::std::tuple_element<I, Tuple>::type& get(
const Tuple& tuple) {
return ::std::get<I>(tuple);
}
};
template <typename... Types>
const size_t TuplePolicy< ::std::tuple<Types...> >::tuple_size;
#endif // GTEST_HAS_STD_TUPLE_
#if GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_
// This helper template allows PrintTo() for tuples and // This helper template allows PrintTo() for tuples and
// UniversalTersePrintTupleFieldsToStrings() to be defined by // UniversalTersePrintTupleFieldsToStrings() to be defined by
// induction on the number of tuple fields. The idea is that // induction on the number of tuple fields. The idea is that
// TuplePrefixPrinter<N>::PrintPrefixTo(t, os) prints the first N // TuplePrefixPrinter<N>::PrintPrefixTo(t, os) prints the first N
// fields in tuple t, and can be defined in terms of // fields in tuple t, and can be defined in terms of
// TuplePrefixPrinter<N - 1>. // TuplePrefixPrinter<N - 1>.
//
// The inductive case. // The inductive case.
template <size_t N> template <size_t N>
struct TuplePrefixPrinter { struct TuplePrefixPrinter {
@ -773,9 +835,12 @@ struct TuplePrefixPrinter {
template <typename Tuple> template <typename Tuple>
static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) { static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) {
TuplePrefixPrinter<N - 1>::PrintPrefixTo(t, os); TuplePrefixPrinter<N - 1>::PrintPrefixTo(t, os);
*os << ", "; if (N > 1) {
UniversalPrinter<typename ::std::tr1::tuple_element<N - 1, Tuple>::type> *os << ", ";
::Print(::std::tr1::get<N - 1>(t), os); }
UniversalPrinter<
typename TuplePolicy<Tuple>::template tuple_element<N - 1>::type>
::Print(TuplePolicy<Tuple>::template get<N - 1>(t), os);
} }
// Tersely prints the first N fields of a tuple to a string vector, // Tersely prints the first N fields of a tuple to a string vector,
@ -784,12 +849,12 @@ struct TuplePrefixPrinter {
static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) { static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) {
TuplePrefixPrinter<N - 1>::TersePrintPrefixToStrings(t, strings); TuplePrefixPrinter<N - 1>::TersePrintPrefixToStrings(t, strings);
::std::stringstream ss; ::std::stringstream ss;
UniversalTersePrint(::std::tr1::get<N - 1>(t), &ss); UniversalTersePrint(TuplePolicy<Tuple>::template get<N - 1>(t), &ss);
strings->push_back(ss.str()); strings->push_back(ss.str());
} }
}; };
// Base cases. // Base case.
template <> template <>
struct TuplePrefixPrinter<0> { struct TuplePrefixPrinter<0> {
template <typename Tuple> template <typename Tuple>
@ -798,34 +863,13 @@ struct TuplePrefixPrinter<0> {
template <typename Tuple> template <typename Tuple>
static void TersePrintPrefixToStrings(const Tuple&, Strings*) {} static void TersePrintPrefixToStrings(const Tuple&, Strings*) {}
}; };
// We have to specialize the entire TuplePrefixPrinter<> class
// template here, even though the definition of
// TersePrintPrefixToStrings() is the same as the generic version, as
// Embarcadero (formerly CodeGear, formerly Borland) C++ doesn't
// support specializing a method template of a class template.
template <>
struct TuplePrefixPrinter<1> {
template <typename Tuple>
static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) {
UniversalPrinter<typename ::std::tr1::tuple_element<0, Tuple>::type>::
Print(::std::tr1::get<0>(t), os);
}
template <typename Tuple> // Helper function for printing a tuple.
static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) { // Tuple must be either std::tr1::tuple or std::tuple type.
::std::stringstream ss; template <typename Tuple>
UniversalTersePrint(::std::tr1::get<0>(t), &ss); void PrintTupleTo(const Tuple& t, ::std::ostream* os) {
strings->push_back(ss.str());
}
};
// Helper function for printing a tuple. T must be instantiated with
// a tuple type.
template <typename T>
void PrintTupleTo(const T& t, ::std::ostream* os) {
*os << "("; *os << "(";
TuplePrefixPrinter< ::std::tr1::tuple_size<T>::value>:: TuplePrefixPrinter<TuplePolicy<Tuple>::tuple_size>::PrintPrefixTo(t, os);
PrintPrefixTo(t, os);
*os << ")"; *os << ")";
} }
@ -835,11 +879,11 @@ void PrintTupleTo(const T& t, ::std::ostream* os) {
template <typename Tuple> template <typename Tuple>
Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) { Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) {
Strings result; Strings result;
TuplePrefixPrinter< ::std::tr1::tuple_size<Tuple>::value>:: TuplePrefixPrinter<TuplePolicy<Tuple>::tuple_size>::
TersePrintPrefixToStrings(value, &result); TersePrintPrefixToStrings(value, &result);
return result; return result;
} }
#endif // GTEST_HAS_TR1_TUPLE #endif // GTEST_HAS_TR1_TUPLE || GTEST_HAS_STD_TUPLE_
} // namespace internal } // namespace internal

View File

@ -343,6 +343,30 @@
# define GTEST_HAS_STD_INITIALIZER_LIST_ 1 # define GTEST_HAS_STD_INITIALIZER_LIST_ 1
#endif #endif
// C++11 specifies that <tuple> provides std::tuple.
// Some platforms still might not have it, however.
#if GTEST_LANG_CXX11
# define GTEST_HAS_STD_TUPLE_ 1
# if defined(__clang__)
// Inspired by http://clang.llvm.org/docs/LanguageExtensions.html#__has_include
# if defined(__has_include) && !__has_include(<tuple>)
# undef GTEST_HAS_STD_TUPLE_
# endif
# elif defined(_MSC_VER)
// Inspired by boost/config/stdlib/dinkumware.hpp
# if defined(_CPPLIB_VER) && _CPPLIB_VER < 520
# undef GTEST_HAS_STD_TUPLE_
# endif
# elif defined(__GLIBCXX__)
// Inspired by boost/config/stdlib/libstdcpp3.hpp,
// http://gcc.gnu.org/gcc-4.2/changes.html and
// http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt01ch01.html#manual.intro.status.standard.200x
# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2)
# undef GTEST_HAS_STD_TUPLE_
# endif
# endif
#endif
// Brings in definitions for functions used in the testing::internal::posix // Brings in definitions for functions used in the testing::internal::posix
// namespace (read, write, close, chdir, isatty, stat). We do not currently // namespace (read, write, close, chdir, isatty, stat). We do not currently
// use them on Windows Mobile. // use them on Windows Mobile.

View File

@ -210,11 +210,6 @@ using ::testing::internal::UniversalTersePrintTupleFieldsToStrings;
using ::testing::internal::kReference; using ::testing::internal::kReference;
using ::testing::internal::string; using ::testing::internal::string;
#if GTEST_HAS_TR1_TUPLE
using ::std::tr1::make_tuple;
using ::std::tr1::tuple;
#endif
// The hash_* classes are not part of the C++ standard. STLport // The hash_* classes are not part of the C++ standard. STLport
// defines them in namespace std. MSVC defines them in ::stdext. GCC // defines them in namespace std. MSVC defines them in ::stdext. GCC
// defines them in ::. // defines them in ::.
@ -984,46 +979,47 @@ TEST(PrintStlContainerTest, ConstIterator) {
} }
#if GTEST_HAS_TR1_TUPLE #if GTEST_HAS_TR1_TUPLE
// Tests printing tuples. // Tests printing ::std::tr1::tuples.
// Tuples of various arities. // Tuples of various arities.
TEST(PrintTupleTest, VariousSizes) { TEST(PrintTr1TupleTest, VariousSizes) {
tuple<> t0; ::std::tr1::tuple<> t0;
EXPECT_EQ("()", Print(t0)); EXPECT_EQ("()", Print(t0));
tuple<int> t1(5); ::std::tr1::tuple<int> t1(5);
EXPECT_EQ("(5)", Print(t1)); EXPECT_EQ("(5)", Print(t1));
tuple<char, bool> t2('a', true); ::std::tr1::tuple<char, bool> t2('a', true);
EXPECT_EQ("('a' (97, 0x61), true)", Print(t2)); EXPECT_EQ("('a' (97, 0x61), true)", Print(t2));
tuple<bool, int, int> t3(false, 2, 3); ::std::tr1::tuple<bool, int, int> t3(false, 2, 3);
EXPECT_EQ("(false, 2, 3)", Print(t3)); EXPECT_EQ("(false, 2, 3)", Print(t3));
tuple<bool, int, int, int> t4(false, 2, 3, 4); ::std::tr1::tuple<bool, int, int, int> t4(false, 2, 3, 4);
EXPECT_EQ("(false, 2, 3, 4)", Print(t4)); EXPECT_EQ("(false, 2, 3, 4)", Print(t4));
tuple<bool, int, int, int, bool> t5(false, 2, 3, 4, true); ::std::tr1::tuple<bool, int, int, int, bool> t5(false, 2, 3, 4, true);
EXPECT_EQ("(false, 2, 3, 4, true)", Print(t5)); EXPECT_EQ("(false, 2, 3, 4, true)", Print(t5));
tuple<bool, int, int, int, bool, int> t6(false, 2, 3, 4, true, 6); ::std::tr1::tuple<bool, int, int, int, bool, int> t6(false, 2, 3, 4, true, 6);
EXPECT_EQ("(false, 2, 3, 4, true, 6)", Print(t6)); EXPECT_EQ("(false, 2, 3, 4, true, 6)", Print(t6));
tuple<bool, int, int, int, bool, int, int> t7(false, 2, 3, 4, true, 6, 7); ::std::tr1::tuple<bool, int, int, int, bool, int, int> t7(
false, 2, 3, 4, true, 6, 7);
EXPECT_EQ("(false, 2, 3, 4, true, 6, 7)", Print(t7)); EXPECT_EQ("(false, 2, 3, 4, true, 6, 7)", Print(t7));
tuple<bool, int, int, int, bool, int, int, bool> t8( ::std::tr1::tuple<bool, int, int, int, bool, int, int, bool> t8(
false, 2, 3, 4, true, 6, 7, true); false, 2, 3, 4, true, 6, 7, true);
EXPECT_EQ("(false, 2, 3, 4, true, 6, 7, true)", Print(t8)); EXPECT_EQ("(false, 2, 3, 4, true, 6, 7, true)", Print(t8));
tuple<bool, int, int, int, bool, int, int, bool, int> t9( ::std::tr1::tuple<bool, int, int, int, bool, int, int, bool, int> t9(
false, 2, 3, 4, true, 6, 7, true, 9); false, 2, 3, 4, true, 6, 7, true, 9);
EXPECT_EQ("(false, 2, 3, 4, true, 6, 7, true, 9)", Print(t9)); EXPECT_EQ("(false, 2, 3, 4, true, 6, 7, true, 9)", Print(t9));
const char* const str = "8"; const char* const str = "8";
// VC++ 2010's implementation of tuple of C++0x is deficient, requiring // VC++ 2010's implementation of tuple of C++0x is deficient, requiring
// an explicit type cast of NULL to be used. // an explicit type cast of NULL to be used.
tuple<bool, char, short, testing::internal::Int32, // NOLINT ::std::tr1::tuple<bool, char, short, testing::internal::Int32, // NOLINT
testing::internal::Int64, float, double, const char*, void*, string> testing::internal::Int64, float, double, const char*, void*, string>
t10(false, 'a', 3, 4, 5, 1.5F, -2.5, str, t10(false, 'a', 3, 4, 5, 1.5F, -2.5, str,
ImplicitCast_<void*>(NULL), "10"); ImplicitCast_<void*>(NULL), "10");
@ -1033,13 +1029,73 @@ TEST(PrintTupleTest, VariousSizes) {
} }
// Nested tuples. // Nested tuples.
TEST(PrintTupleTest, NestedTuple) { TEST(PrintTr1TupleTest, NestedTuple) {
tuple<tuple<int, bool>, char> nested(make_tuple(5, true), 'a'); ::std::tr1::tuple< ::std::tr1::tuple<int, bool>, char> nested(
::std::tr1::make_tuple(5, true), 'a');
EXPECT_EQ("((5, true), 'a' (97, 0x61))", Print(nested)); EXPECT_EQ("((5, true), 'a' (97, 0x61))", Print(nested));
} }
#endif // GTEST_HAS_TR1_TUPLE #endif // GTEST_HAS_TR1_TUPLE
#if GTEST_LANG_CXX11
// Tests printing ::std::tuples.
// Tuples of various arities.
TEST(PrintStdTupleTest, VariousSizes) {
::std::tuple<> t0;
EXPECT_EQ("()", Print(t0));
::std::tuple<int> t1(5);
EXPECT_EQ("(5)", Print(t1));
::std::tuple<char, bool> t2('a', true);
EXPECT_EQ("('a' (97, 0x61), true)", Print(t2));
::std::tuple<bool, int, int> t3(false, 2, 3);
EXPECT_EQ("(false, 2, 3)", Print(t3));
::std::tuple<bool, int, int, int> t4(false, 2, 3, 4);
EXPECT_EQ("(false, 2, 3, 4)", Print(t4));
::std::tuple<bool, int, int, int, bool> t5(false, 2, 3, 4, true);
EXPECT_EQ("(false, 2, 3, 4, true)", Print(t5));
::std::tuple<bool, int, int, int, bool, int> t6(false, 2, 3, 4, true, 6);
EXPECT_EQ("(false, 2, 3, 4, true, 6)", Print(t6));
::std::tuple<bool, int, int, int, bool, int, int> t7(
false, 2, 3, 4, true, 6, 7);
EXPECT_EQ("(false, 2, 3, 4, true, 6, 7)", Print(t7));
::std::tuple<bool, int, int, int, bool, int, int, bool> t8(
false, 2, 3, 4, true, 6, 7, true);
EXPECT_EQ("(false, 2, 3, 4, true, 6, 7, true)", Print(t8));
::std::tuple<bool, int, int, int, bool, int, int, bool, int> t9(
false, 2, 3, 4, true, 6, 7, true, 9);
EXPECT_EQ("(false, 2, 3, 4, true, 6, 7, true, 9)", Print(t9));
const char* const str = "8";
// VC++ 2010's implementation of tuple of C++0x is deficient, requiring
// an explicit type cast of NULL to be used.
::std::tuple<bool, char, short, testing::internal::Int32, // NOLINT
testing::internal::Int64, float, double, const char*, void*, string>
t10(false, 'a', 3, 4, 5, 1.5F, -2.5, str,
ImplicitCast_<void*>(NULL), "10");
EXPECT_EQ("(false, 'a' (97, 0x61), 3, 4, 5, 1.5, -2.5, " + PrintPointer(str) +
" pointing to \"8\", NULL, \"10\")",
Print(t10));
}
// Nested tuples.
TEST(PrintStdTupleTest, NestedTuple) {
::std::tuple< ::std::tuple<int, bool>, char> nested(
::std::make_tuple(5, true), 'a');
EXPECT_EQ("((5, true), 'a' (97, 0x61))", Print(nested));
}
#endif // GTEST_LANG_CXX11
// Tests printing user-defined unprintable types. // Tests printing user-defined unprintable types.
// Unprintable types in the global namespace. // Unprintable types in the global namespace.
@ -1532,28 +1588,31 @@ TEST(UniversalPrintTest, WorksForCharArray) {
#if GTEST_HAS_TR1_TUPLE #if GTEST_HAS_TR1_TUPLE
TEST(UniversalTersePrintTupleFieldsToStringsTest, PrintsEmptyTuple) { TEST(UniversalTersePrintTupleFieldsToStringsTestWithTr1, PrintsEmptyTuple) {
Strings result = UniversalTersePrintTupleFieldsToStrings(make_tuple()); Strings result = UniversalTersePrintTupleFieldsToStrings(
::std::tr1::make_tuple());
EXPECT_EQ(0u, result.size()); EXPECT_EQ(0u, result.size());
} }
TEST(UniversalTersePrintTupleFieldsToStringsTest, PrintsOneTuple) { TEST(UniversalTersePrintTupleFieldsToStringsTestWithTr1, PrintsOneTuple) {
Strings result = UniversalTersePrintTupleFieldsToStrings(make_tuple(1)); Strings result = UniversalTersePrintTupleFieldsToStrings(
::std::tr1::make_tuple(1));
ASSERT_EQ(1u, result.size()); ASSERT_EQ(1u, result.size());
EXPECT_EQ("1", result[0]); EXPECT_EQ("1", result[0]);
} }
TEST(UniversalTersePrintTupleFieldsToStringsTest, PrintsTwoTuple) { TEST(UniversalTersePrintTupleFieldsToStringsTestWithTr1, PrintsTwoTuple) {
Strings result = UniversalTersePrintTupleFieldsToStrings(make_tuple(1, 'a')); Strings result = UniversalTersePrintTupleFieldsToStrings(
::std::tr1::make_tuple(1, 'a'));
ASSERT_EQ(2u, result.size()); ASSERT_EQ(2u, result.size());
EXPECT_EQ("1", result[0]); EXPECT_EQ("1", result[0]);
EXPECT_EQ("'a' (97, 0x61)", result[1]); EXPECT_EQ("'a' (97, 0x61)", result[1]);
} }
TEST(UniversalTersePrintTupleFieldsToStringsTest, PrintsTersely) { TEST(UniversalTersePrintTupleFieldsToStringsTestWithTr1, PrintsTersely) {
const int n = 1; const int n = 1;
Strings result = UniversalTersePrintTupleFieldsToStrings( Strings result = UniversalTersePrintTupleFieldsToStrings(
tuple<const int&, const char*>(n, "a")); ::std::tr1::tuple<const int&, const char*>(n, "a"));
ASSERT_EQ(2u, result.size()); ASSERT_EQ(2u, result.size());
EXPECT_EQ("1", result[0]); EXPECT_EQ("1", result[0]);
EXPECT_EQ("\"a\"", result[1]); EXPECT_EQ("\"a\"", result[1]);
@ -1561,5 +1620,38 @@ TEST(UniversalTersePrintTupleFieldsToStringsTest, PrintsTersely) {
#endif // GTEST_HAS_TR1_TUPLE #endif // GTEST_HAS_TR1_TUPLE
#if GTEST_HAS_STD_TUPLE_
TEST(UniversalTersePrintTupleFieldsToStringsTestWithStd, PrintsEmptyTuple) {
Strings result = UniversalTersePrintTupleFieldsToStrings(::std::make_tuple());
EXPECT_EQ(0u, result.size());
}
TEST(UniversalTersePrintTupleFieldsToStringsTestWithStd, PrintsOneTuple) {
Strings result = UniversalTersePrintTupleFieldsToStrings(
::std::make_tuple(1));
ASSERT_EQ(1u, result.size());
EXPECT_EQ("1", result[0]);
}
TEST(UniversalTersePrintTupleFieldsToStringsTestWithStd, PrintsTwoTuple) {
Strings result = UniversalTersePrintTupleFieldsToStrings(
::std::make_tuple(1, 'a'));
ASSERT_EQ(2u, result.size());
EXPECT_EQ("1", result[0]);
EXPECT_EQ("'a' (97, 0x61)", result[1]);
}
TEST(UniversalTersePrintTupleFieldsToStringsTestWithStd, PrintsTersely) {
const int n = 1;
Strings result = UniversalTersePrintTupleFieldsToStrings(
::std::tuple<const int&, const char*>(n, "a"));
ASSERT_EQ(2u, result.size());
EXPECT_EQ("1", result[0]);
EXPECT_EQ("\"a\"", result[1]);
}
#endif // GTEST_HAS_STD_TUPLE_
} // namespace gtest_printers_test } // namespace gtest_printers_test
} // namespace testing } // namespace testing