mirror of
https://github.com/google/googletest.git
synced 2024-12-27 18:31:21 +08:00
Merge pull request #1186 from Dani-Hub/master
Prevent infinite loops for recursive containers like boost::filesystem::path
This commit is contained in:
commit
eabd5c908d
@ -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<sizeof(IsContainerTest<T>(0)) == sizeof(IsContainer)
|
||||
? kPrintContainer : !is_pointer<T>::value
|
||||
? kPrintOther
|
||||
WrapPrinterType<
|
||||
(sizeof(IsContainerTest<T>(0)) == sizeof(IsContainer)) && !IsRecursiveContainer<T>::value
|
||||
? kPrintContainer : !is_pointer<T>::value
|
||||
? kPrintOther
|
||||
#if GTEST_LANG_CXX11
|
||||
: std::is_function<typename std::remove_pointer<T>::type>::value
|
||||
#else
|
||||
: !internal::ImplicitlyConvertible<T, const void*>::value
|
||||
#endif
|
||||
? kPrintFunctionPointer
|
||||
: kPrintPointer>(),
|
||||
? kPrintFunctionPointer
|
||||
: kPrintPointer>(),
|
||||
value, os);
|
||||
}
|
||||
|
||||
|
@ -940,6 +940,31 @@ typedef char IsNotContainer;
|
||||
template <class C>
|
||||
IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; }
|
||||
|
||||
template <typename C, bool =
|
||||
sizeof(IsContainerTest<C>(0)) == sizeof(IsContainer)
|
||||
>
|
||||
struct IsRecursiveContainerImpl;
|
||||
|
||||
template <typename C>
|
||||
struct IsRecursiveContainerImpl<C, false> : public false_type {};
|
||||
|
||||
template <typename C>
|
||||
struct IsRecursiveContainerImpl<C, true> {
|
||||
typedef
|
||||
typename IteratorTraits<typename C::iterator>::value_type
|
||||
value_type;
|
||||
typedef is_same<value_type, C> type;
|
||||
};
|
||||
|
||||
// IsRecursiveContainer<Type> 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<typename C>
|
||||
struct IsRecursiveContainer : public IsRecursiveContainerImpl<C>::type {};
|
||||
|
||||
// EnableIf<condition>::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
|
||||
|
@ -2242,6 +2242,12 @@ template <bool bool_value> const bool bool_constant<bool_value>::value;
|
||||
typedef bool_constant<false> false_type;
|
||||
typedef bool_constant<true> true_type;
|
||||
|
||||
template <typename T, typename U>
|
||||
struct is_same : public false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_same<T, T> : public true_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_pointer : public false_type {};
|
||||
|
||||
|
@ -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<int>()));
|
||||
}
|
||||
|
||||
// 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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user