Googletest export
Improve lookup of operator<< for user types Without this fix, trying to use this class with googletest struct Foo {}; template <typename OutputStream> OutputStream& operator<<(OutputStream& os, const Foo&) { os << "TemplatedStreamableInFoo"; return os; } results in an ambiguity error between the class' operator<< and the operator<< in gtest-printers.h removed in this CL. This fix also enables implicit conversions to happen, so that e.g. we will find the base class operator<< if a subclass has no operator<< of its own. PiperOrigin-RevId: 336261221
This commit is contained in:
parent
72512aa893
commit
0555b0eacb
@ -192,43 +192,34 @@ struct PointerPrinter {
|
||||
}
|
||||
};
|
||||
|
||||
namespace internal_stream {
|
||||
namespace internal_stream_operator_without_lexical_name_lookup {
|
||||
|
||||
struct Sentinel;
|
||||
template <typename Char, typename CharTraits, typename T>
|
||||
Sentinel* operator<<(::std::basic_ostream<Char, CharTraits>& os, const T& x);
|
||||
|
||||
// Check if the user has a user-defined operator<< for their type.
|
||||
//
|
||||
// We put this in its own namespace to inject a custom operator<< that allows us
|
||||
// to probe the type's operator.
|
||||
//
|
||||
// Note that this operator<< takes a generic std::basic_ostream<Char,
|
||||
// CharTraits> type instead of the more restricted std::ostream. If
|
||||
// we define it to take an std::ostream instead, we'll get an
|
||||
// "ambiguous overloads" compiler error when trying to print a type
|
||||
// Foo that supports streaming to std::basic_ostream<Char,
|
||||
// CharTraits>, as the compiler cannot tell whether
|
||||
// operator<<(std::ostream&, const T&) or
|
||||
// operator<<(std::basic_stream<Char, CharTraits>, const Foo&) is more
|
||||
// specific.
|
||||
template <typename T>
|
||||
constexpr bool UseStreamOperator() {
|
||||
return !std::is_same<decltype(std::declval<std::ostream&>()
|
||||
<< std::declval<const T&>()),
|
||||
Sentinel*>::value;
|
||||
}
|
||||
|
||||
} // namespace internal_stream
|
||||
// The presence of an operator<< here will terminate lexical scope lookup
|
||||
// straight away (even though it cannot be a match because of its argument
|
||||
// types). Thus, the two operator<< calls in StreamPrinter will find only ADL
|
||||
// candidates.
|
||||
struct LookupBlocker {};
|
||||
void operator<<(LookupBlocker, LookupBlocker);
|
||||
|
||||
struct StreamPrinter {
|
||||
template <typename T, typename = typename std::enable_if<
|
||||
internal_stream::UseStreamOperator<T>()>::type>
|
||||
template <typename T,
|
||||
// Don't accept member pointers here. We'd print them via implicit
|
||||
// conversion to bool, which isn't useful.
|
||||
typename = typename std::enable_if<
|
||||
!std::is_member_pointer<T>::value>::type,
|
||||
// Only accept types for which we can find a streaming operator via
|
||||
// ADL (possibly involving implicit conversions).
|
||||
typename = decltype(std::declval<std::ostream&>()
|
||||
<< std::declval<const T&>())>
|
||||
static void PrintValue(const T& value, ::std::ostream* os) {
|
||||
// Call streaming operator found by ADL, possibly with implicit conversions
|
||||
// of the arguments.
|
||||
*os << value;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal_stream_operator_without_lexical_name_lookup
|
||||
|
||||
struct ProtobufPrinter {
|
||||
// We print a protobuf using its ShortDebugString() when the string
|
||||
// doesn't exceed this many characters; otherwise we print it using
|
||||
@ -308,7 +299,8 @@ template <typename T>
|
||||
void PrintWithFallback(const T& value, ::std::ostream* os) {
|
||||
using Printer = typename FindFirstPrinter<
|
||||
T, void, ContainerPrinter, FunctionPointerPrinter, PointerPrinter,
|
||||
StreamPrinter, ProtobufPrinter, ConvertibleToIntegerPrinter,
|
||||
internal_stream_operator_without_lexical_name_lookup::StreamPrinter,
|
||||
ProtobufPrinter, ConvertibleToIntegerPrinter,
|
||||
ConvertibleToStringViewPrinter, FallbackPrinter>::type;
|
||||
Printer::PrintValue(value, os);
|
||||
}
|
||||
|
@ -90,6 +90,18 @@ class BiggestIntConvertible {
|
||||
operator ::testing::internal::BiggestInt() const { return 42; }
|
||||
};
|
||||
|
||||
// A parent class with two child classes. The parent and one of the kids have
|
||||
// stream operators.
|
||||
class ParentClass {};
|
||||
class ChildClassWithStreamOperator : public ParentClass {};
|
||||
class ChildClassWithoutStreamOperator : public ParentClass {};
|
||||
static void operator<<(std::ostream& os, const ParentClass&) {
|
||||
os << "ParentClass";
|
||||
}
|
||||
static void operator<<(std::ostream& os, const ChildClassWithStreamOperator&) {
|
||||
os << "ChildClassWithStreamOperator";
|
||||
}
|
||||
|
||||
// A user-defined unprintable class template in the global namespace.
|
||||
template <typename T>
|
||||
class UnprintableTemplateInGlobal {
|
||||
@ -177,6 +189,17 @@ inline ::std::ostream& operator<<(::std::ostream& os,
|
||||
return os << "StreamableTemplateInFoo: " << x.value();
|
||||
}
|
||||
|
||||
// A user-defined streamable type in a user namespace whose operator<< is
|
||||
// templated on the type of the output stream.
|
||||
struct TemplatedStreamableInFoo {};
|
||||
|
||||
template <typename OutputStream>
|
||||
OutputStream& operator<<(OutputStream& os,
|
||||
const TemplatedStreamableInFoo& /*ts*/) {
|
||||
os << "TemplatedStreamableInFoo";
|
||||
return os;
|
||||
}
|
||||
|
||||
// A user-defined streamable but recursivly-defined container type in
|
||||
// a user namespace, it mimics therefore std::filesystem::path or
|
||||
// boost::filesystem::path.
|
||||
@ -1201,6 +1224,20 @@ TEST(PrintStreamableTypeTest, TemplateTypeInUserNamespace) {
|
||||
Print(::foo::StreamableTemplateInFoo<int>()));
|
||||
}
|
||||
|
||||
TEST(PrintStreamableTypeTest, TypeInUserNamespaceWithTemplatedStreamOperator) {
|
||||
EXPECT_EQ("TemplatedStreamableInFoo",
|
||||
Print(::foo::TemplatedStreamableInFoo()));
|
||||
}
|
||||
|
||||
TEST(PrintStreamableTypeTest, SubclassUsesSuperclassStreamOperator) {
|
||||
ParentClass parent;
|
||||
ChildClassWithStreamOperator child_stream;
|
||||
ChildClassWithoutStreamOperator child_no_stream;
|
||||
EXPECT_EQ("ParentClass", Print(parent));
|
||||
EXPECT_EQ("ChildClassWithStreamOperator", Print(child_stream));
|
||||
EXPECT_EQ("ParentClass", Print(child_no_stream));
|
||||
}
|
||||
|
||||
// Tests printing a user-defined recursive container type that has a <<
|
||||
// operator.
|
||||
TEST(PrintStreamableTypeTest, PathLikeInUserNamespace) {
|
||||
|
Loading…
Reference in New Issue
Block a user