diff --git a/googletest/include/gtest/gtest-printers.h b/googletest/include/gtest/gtest-printers.h index e850d605..fba76614 100644 --- a/googletest/include/gtest/gtest-printers.h +++ b/googletest/include/gtest/gtest-printers.h @@ -460,15 +460,17 @@ void PrintTo(const T& value, ::std::ostream* os) { // DefaultPrintTo() is overloaded. The type of its first argument // determines which version will be picked. // - // Note that we check for container types here, prior to we check - // for protocol message types in our operator<<. The rationale is: + // Note that we check for recursive and other container types here, prior + // to we check for protocol message types in our operator<<. The rationale is: // // For protocol messages, we want to give people a chance to // override Google Mock's format by defining a PrintTo() or // operator<<. For STL containers, other formats can be // incompatible with Google Mock's format for the container // elements; therefore we check for container types here to ensure - // that our format is used. + // that our format is used. To prevent an infinite runtime recursion + // during the output of recursive container types, we check first for + // those. // // Note that MSVC and clang-cl do allow an implicit conversion from // pointer-to-function to pointer-to-object, but clang-cl warns on it. @@ -477,16 +479,17 @@ void PrintTo(const T& value, ::std::ostream* os) { // function pointers so that the `*os << p` in the object pointer overload // doesn't cause that warning either. DefaultPrintTo( - WrapPrinterType(0)) == sizeof(IsContainer) - ? kPrintContainer : !is_pointer::value - ? kPrintOther + WrapPrinterType< + (sizeof(IsContainerTest(0)) == sizeof(IsContainer)) && !IsRecursiveContainer::value + ? kPrintContainer : !is_pointer::value + ? kPrintOther #if GTEST_LANG_CXX11 : std::is_function::type>::value #else : !internal::ImplicitlyConvertible::value #endif - ? kPrintFunctionPointer - : kPrintPointer>(), + ? kPrintFunctionPointer + : kPrintPointer>(), value, os); } diff --git a/googletest/include/gtest/internal/gtest-internal.h b/googletest/include/gtest/internal/gtest-internal.h index 72d83f0b..2a6e4dad 100644 --- a/googletest/include/gtest/internal/gtest-internal.h +++ b/googletest/include/gtest/internal/gtest-internal.h @@ -940,6 +940,31 @@ typedef char IsNotContainer; template IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; } +template (0)) == sizeof(IsContainer) +> +struct IsRecursiveContainerImpl; + +template +struct IsRecursiveContainerImpl : public false_type {}; + +template +struct IsRecursiveContainerImpl { + typedef + typename IteratorTraits::value_type + value_type; + typedef is_same type; +}; + +// IsRecursiveContainer is a unary compile-time predicate that +// evaluates whether C is a recursive container type. A recursive container +// type is a container type whose value_type is equal to the container type +// itself. An example for a recursive container type is +// boost::filesystem::path, whose iterator has a value_type that is equal to +// boost::filesystem::path. +template +struct IsRecursiveContainer : public IsRecursiveContainerImpl::type {}; + // EnableIf::type is void when 'Cond' is true, and // undefined when 'Cond' is false. To use SFINAE to make a function // overload only apply when a particular expression is true, add diff --git a/googletest/include/gtest/internal/gtest-port.h b/googletest/include/gtest/internal/gtest-port.h index 448c4c86..c2a9f5f3 100644 --- a/googletest/include/gtest/internal/gtest-port.h +++ b/googletest/include/gtest/internal/gtest-port.h @@ -2242,6 +2242,12 @@ template const bool bool_constant::value; typedef bool_constant false_type; typedef bool_constant true_type; +template +struct is_same : public false_type {}; + +template +struct is_same : public true_type {}; + template struct is_pointer : public false_type {}; diff --git a/googletest/test/gtest-printers_test.cc b/googletest/test/gtest-printers_test.cc index b0a83410..487d3cb3 100644 --- a/googletest/test/gtest-printers_test.cc +++ b/googletest/test/gtest-printers_test.cc @@ -187,6 +187,29 @@ inline ::std::ostream& operator<<(::std::ostream& os, return os << "StreamableTemplateInFoo: " << x.value(); } +// A user-defined streamable but recursivly-defined container type in +// a user namespace, it mimics therefore std::filesystem::path or +// boost::filesystem::path. +class PathLike { + public: + struct iterator + { + typedef PathLike value_type; + }; + typedef iterator const_iterator; + + PathLike() {} + + iterator begin() const { return iterator(); } + iterator end() const { return iterator(); } + + friend + ::std::ostream& operator<<(::std::ostream& os, const PathLike&) + { + return os << "Streamable-PathLike"; + } +}; + } // namespace foo namespace testing { @@ -1161,6 +1184,15 @@ TEST(PrintStreamableTypeTest, TemplateTypeInUserNamespace) { Print(::foo::StreamableTemplateInFoo())); } +// Tests printing a user-defined recursive container type that has a << +// operator. +TEST(PrintStreamableTypeTest, PathLikeInUserNamespace) { + ::foo::PathLike x; + EXPECT_EQ("Streamable-PathLike", Print(x)); + const ::foo::PathLike cx; + EXPECT_EQ("Streamable-PathLike", Print(cx)); +} + // Tests printing user-defined types that have a PrintTo() function. TEST(PrintPrintableTypeTest, InUserNamespace) { EXPECT_EQ("PrintableViaPrintTo: 0",