From ae90305ed586b733cf6df07620b4655d210017ae Mon Sep 17 00:00:00 2001 From: MakersF Date: Sun, 5 Dec 2021 16:50:37 +0000 Subject: [PATCH 1/3] Add test for tuples with different reference categories Currently there is no test which checks that tuple with various reference categories can successfully be printed. Tuples like that can happen when mocking a function which takes refererences --- googletest/test/googletest-printers-test.cc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/googletest/test/googletest-printers-test.cc b/googletest/test/googletest-printers-test.cc index 0058917a..f708e908 100644 --- a/googletest/test/googletest-printers-test.cc +++ b/googletest/test/googletest-printers-test.cc @@ -1247,6 +1247,22 @@ TEST(PrintStdTupleTest, VariousSizes) { Print(t10)); } +// Tuple with various qualifiers +TEST(PrintStdTupleTest, VariousQualifiers) { + ::std::tuple t0{1, 2}; + EXPECT_EQ("(1, 2)", Print(t0)); + int i1 = 3; + int i2 = 4; + ::std::tuple t1{i1, i2}; + EXPECT_EQ("(@" + PrintPointer(&i1) + " 3, @" + PrintPointer(&i2) + " 4)", + Print(t1)); + int i3 = 5; + int i4 = 6; + ::std::tuple t2{static_cast(i3), + static_cast(i4)}; + EXPECT_EQ("(5, 6)", Print(t2)); +} + // Nested tuples. TEST(PrintStdTupleTest, NestedTuple) { ::std::tuple< ::std::tuple, char> nested( From 10da596ac9f38ce272aad1375451f153fbe8bd54 Mon Sep 17 00:00:00 2001 From: MakersF Date: Sun, 5 Dec 2021 16:54:38 +0000 Subject: [PATCH 2/3] Add test case for object with templated PrintTo on generic type When PrintTo is defined on a set of types which are accepted with a compile time predicate (using SFINAE), the current implementation fails to compile due to an ambiguous error. --- googletest/test/googletest-printers-test.cc | 28 +++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/googletest/test/googletest-printers-test.cc b/googletest/test/googletest-printers-test.cc index f708e908..363d806c 100644 --- a/googletest/test/googletest-printers-test.cc +++ b/googletest/test/googletest-printers-test.cc @@ -85,6 +85,15 @@ void PrintTo(EnumWithPrintTo e, std::ostream* os) { *os << (e == kEWPT1 ? "kEWPT1" : "invalid"); } +struct StructWithSFINAETemplatePrintToInGlobal {}; + +template ::value>::type> +void PrintTo(const T& obj, std::ostream* os) { + *os << "StructWithSFINAETemplatePrintToInGlobal"; +} + // A class implicitly convertible to BiggestInt. class BiggestIntConvertible { public: @@ -173,6 +182,15 @@ void PrintTo(const PrintableViaPrintToTemplate& x, ::std::ostream* os) { *os << "PrintableViaPrintToTemplate: " << x.value(); } +struct StructWithSFINAETemplatePrintToInFoo {}; + +template ::value>::type> +void PrintTo(const T& obj, std::ostream* os) { + *os << "StructWithSFINAETemplatePrintToInFoo"; +} + // A user-defined streamable class template in a user namespace. template class StreamableTemplateInFoo { @@ -1292,6 +1310,16 @@ TEST(PrintReferenceWrapper, Unprintable) { Print(std::cref(up))); } +TEST(PrintPrintableTypeWithSfinaePrintTo, InGlobalNamespace) { + EXPECT_EQ("StructWithSFINAETemplatePrintToInGlobal", + Print(StructWithSFINAETemplatePrintToInGlobal())); +} + +TEST(PrintPrintableTypeWithSfinaePrintTo, InFooNamespace) { + EXPECT_EQ("StructWithSFINAETemplatePrintToInFoo", + Print(StructWithSFINAETemplatePrintToInFoo())); +} + // Tests printing user-defined unprintable types. // Unprintable types in the global namespace. From 14a80a902746b80c596a976a51bd0b736e0ca781 Mon Sep 17 00:00:00 2001 From: MakersF Date: Sun, 5 Dec 2021 17:46:10 +0000 Subject: [PATCH 3/3] Enable `PrintTo` template overloads to be defined by the user A user cannot define a template for `PrintTo` which just takes the type directly. This is because gtest already defines an implementation for the function, which prints the type with the fallback strategy. This prevents users from providing a `PrintTo` function like ``` template> void PrintTo(const T&, std::ostream*) { ... } ``` as it's ambiguous with the `PrintTo` template defined by gtest. This change resolves this. The current behaviour depends on the overload resolution priority for calling the right `PrintTo` function. This change removes the default implementation of `PrintTo`, and instead detects whether there is a `PrintTo` function that can be called with the type. If it exists, it calls that, otherwise it uses the fallback behaviour. Unfortunately it's not that simple: due to implicit conversions, once the `PrintTo` (unconstrained) template function is removed, many types will implicitly convert and use one of the other overloads of `PrintTo` defined by gtest, while before it would have used the unconstrained template, as it was a better match. To address this, this change checks whether the type would ahve called the unconstrained template, if it existed. In such situations, the fallback behaviour will be used, to remain backward compatible. To check if the unconstrained template would have been called, such a function is added in a private namespace, and `decltype` is used to detect which overload would have been called. --- googletest/include/gtest/gtest-printers.h | 132 ++++++++++++++---- .../include/gtest/internal/gtest-internal.h | 34 +++++ googletest/test/googletest-printers-test.cc | 2 +- 3 files changed, 137 insertions(+), 31 deletions(-) diff --git a/googletest/include/gtest/gtest-printers.h b/googletest/include/gtest/gtest-printers.h index e07b537d..63f3950e 100644 --- a/googletest/include/gtest/gtest-printers.h +++ b/googletest/include/gtest/gtest-printers.h @@ -420,13 +420,10 @@ std::string FormatForComparisonFailureMessage( // We define UniversalPrinter as a class template (as opposed to a // function template), as we need to partially specialize it for // reference types, which cannot be done with function templates. -template -class UniversalPrinter; - -// Prints the given value using the << operator if it has one; -// otherwise prints the bytes in it. This is what -// UniversalPrinter::Print() does when PrintTo() is not specialized -// or overloaded for type T. +// +// When PrintTo() is not specialized or overloaded for a type T, +// UniversalPrinter::Print prints the given value using the +// << operator if it has one; otherwise prints the bytes in it. // // A user can override this behavior for a class type Foo by defining // an overload of PrintTo() in the namespace where Foo is defined. We @@ -435,9 +432,7 @@ class UniversalPrinter; // or there is already a << operator but it doesn't do what the user // wants). template -void PrintTo(const T& value, ::std::ostream* os) { - internal::PrintWithFallback(value, os); -} +class UniversalPrinter; // The following list of PrintTo() overloads tells // UniversalPrinter::Print() how to print standard types (built-in @@ -450,7 +445,7 @@ inline void PrintTo(char c, ::std::ostream* os) { // When printing a plain char, we always treat it as unsigned. This // way, the output won't be affected by whether the compiler thinks // char is signed or not. - PrintTo(static_cast(c), os); + UniversalPrint(static_cast(c), os); } // Overloads for other simple built-in types. @@ -469,11 +464,11 @@ GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os); GTEST_API_ void PrintTo(char32_t c, ::std::ostream* os); inline void PrintTo(char16_t c, ::std::ostream* os) { - PrintTo(ImplicitCast_(c), os); + UniversalPrint(ImplicitCast_(c), os); } #ifdef __cpp_char8_t inline void PrintTo(char8_t c, ::std::ostream* os) { - PrintTo(ImplicitCast_(c), os); + UniversalPrint(ImplicitCast_(c), os); } #endif @@ -486,39 +481,39 @@ GTEST_API_ void PrintTo(__int128_t v, ::std::ostream* os); // Overloads for C strings. GTEST_API_ void PrintTo(const char* s, ::std::ostream* os); inline void PrintTo(char* s, ::std::ostream* os) { - PrintTo(ImplicitCast_(s), os); + UniversalPrint(ImplicitCast_(s), os); } // signed/unsigned char is often used for representing binary data, so // we print pointers to it as void* to be safe. inline void PrintTo(const signed char* s, ::std::ostream* os) { - PrintTo(ImplicitCast_(s), os); + UniversalPrint(ImplicitCast_(s), os); } inline void PrintTo(signed char* s, ::std::ostream* os) { - PrintTo(ImplicitCast_(s), os); + UniversalPrint(ImplicitCast_(s), os); } inline void PrintTo(const unsigned char* s, ::std::ostream* os) { - PrintTo(ImplicitCast_(s), os); + UniversalPrint(ImplicitCast_(s), os); } inline void PrintTo(unsigned char* s, ::std::ostream* os) { - PrintTo(ImplicitCast_(s), os); + UniversalPrint(ImplicitCast_(s), os); } #ifdef __cpp_char8_t // Overloads for u8 strings. GTEST_API_ void PrintTo(const char8_t* s, ::std::ostream* os); inline void PrintTo(char8_t* s, ::std::ostream* os) { - PrintTo(ImplicitCast_(s), os); + UniversalPrint(ImplicitCast_(s), os); } #endif // Overloads for u16 strings. GTEST_API_ void PrintTo(const char16_t* s, ::std::ostream* os); inline void PrintTo(char16_t* s, ::std::ostream* os) { - PrintTo(ImplicitCast_(s), os); + UniversalPrint(ImplicitCast_(s), os); } // Overloads for u32 strings. GTEST_API_ void PrintTo(const char32_t* s, ::std::ostream* os); inline void PrintTo(char32_t* s, ::std::ostream* os) { - PrintTo(ImplicitCast_(s), os); + UniversalPrint(ImplicitCast_(s), os); } // MSVC can be configured to define wchar_t as a typedef of unsigned @@ -530,7 +525,7 @@ inline void PrintTo(char32_t* s, ::std::ostream* os) { // Overloads for wide C strings GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os); inline void PrintTo(wchar_t* s, ::std::ostream* os) { - PrintTo(ImplicitCast_(s), os); + UniversalPrint(ImplicitCast_(s), os); } #endif @@ -585,7 +580,7 @@ inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) { #if GTEST_INTERNAL_HAS_STRING_VIEW // Overload for internal::StringView. inline void PrintTo(internal::StringView sp, ::std::ostream* os) { - PrintTo(::std::string(sp), os); + UniversalPrint(::std::string(sp), os); } #endif // GTEST_INTERNAL_HAS_STRING_VIEW @@ -677,30 +672,107 @@ void PrintTo(const ::std::pair& value, ::std::ostream* os) { *os << ')'; } +namespace universal_printer_impl { + +namespace unconstrained_print_to { +// Create a PrintTo which matches anything, and returns a type that +// no other PrintTo overload can return +struct SentinelType {}; +template +SentinelType PrintTo(const T&, ::std::ostream* os); +} // namespace unconstrained_print_to + +namespace print_to_with_unconstrained_print { +// Bring in all the PrintTo overloads in the same namespace +// so they have the same priority (together with the ADL one) +using internal::PrintTo; +using unconstrained_print_to::PrintTo; + +// Detect the returned type of calling PrintTo in the current context. +// It can result in 3 outcomes: +// 1. There is a better match than the unconstrained PrintTo. +// It will evalute to whatever that returns, likely `void`. +// 2. There isn't a better match than the unconstrained PrintTo we defined. +// It will evaluate to `SentinelType`. +// 3. The call is ambiguous as since there are multiple valid overloads. +// This will trigger a template substitution failure +template +using PrintToOverloadResult = decltype(PrintTo( + ::std::declval(), ::std::declval<::std::ostream*>())); + +} // namespace print_to_with_unconstrained_print + +// This is going to be: +// 1. True: when the unconstrained PrintTo would be the preferred one, if it +// existed +// 2. False: when there is a PrintTo that is preferred over the unconstrained +// one, or if the call is ambiguous +template +using PrefersFallbackBehaviour = ::std::is_same< + DetectedType, + unconstrained_print_to::SentinelType>; + +} // namespace universal_printer_impl + // Implements printing a non-reference type T by letting the compiler // pick the right overload of PrintTo() for T. -template +template class UniversalPrinter { public: // MSVC warns about adding const to a function type, so we want to // disable the warning. GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180) + // const T& becomes U& when T = U&&. + // This creates an ambiguity in PrintImpl, so to avoid it we + // remove the qualifiers from the type. + using T = GTEST_REMOVE_REFERENCE_AND_CONST_(QualifiedT); + // Note: we deliberately don't call this PrintTo(), as that name // conflicts with ::testing::internal::PrintTo in the body of the // function. static void Print(const T& value, ::std::ostream* os) { - // By default, ::testing::internal::PrintTo() is used for printing - // the value. - // + // Select the correct overload depending on whether PrintTo for + // type exists and it's valid. + PrintImpl(value, os, HigherPriorityTag()); + } + + private: + // Used to disambiguate between the 2 functions below, giving precedence + // to the one using PrintTo + struct LowerPriorityTag {}; + struct HigherPriorityTag : LowerPriorityTag {}; + + // We want to use PrintTo if: + // 1. It exists and can be called unambiguously. + // This is checked with the `decltype` in the return type, and + // 2. Before this change, the type was *not* using the fallback + // behaviour. This is checked in the `enable_if`. + // Note that the `enable_if` will be true even when the `PrintTo` + // call is ambiguous. If it's ambiguous when evaluated in the current + // context, the first check will fail to compile, and the fallback + // function will be used. + // If it's ambiguous only when the unconstrained + // `PrintTo` is added, then it means that a user defined an + // unconstrained template, and we want to use that. + template < + class U = T, + class = typename std::enable_if< + !universal_printer_impl::PrefersFallbackBehaviour::value>::type> + static auto PrintImpl(const U& value, ::std::ostream* os, HigherPriorityTag) + -> decltype((void)PrintTo(value, os)) { // Thanks to Koenig look-up, if T is a class and has its own // PrintTo() function defined in its namespace, that function will - // be visible here. Since it is more specific than the generic ones - // in ::testing::internal, it will be picked by the compiler in the - // following statement - exactly what we want. + // be visible here. PrintTo(value, os); } + static void PrintImpl(const T& value, ::std::ostream* os, LowerPriorityTag) { + // By default, ::testing::internal::PrintWithFallback() is used for + // printing the value. + internal::PrintWithFallback(value, os); + } + GTEST_DISABLE_MSC_WARNINGS_POP_() }; diff --git a/googletest/include/gtest/internal/gtest-internal.h b/googletest/include/gtest/internal/gtest-internal.h index 4e66667d..cdd5a28c 100644 --- a/googletest/include/gtest/internal/gtest-internal.h +++ b/googletest/include/gtest/internal/gtest-internal.h @@ -886,6 +886,8 @@ class GTEST_API_ Random { #define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \ typename std::remove_const::type>::type + + // HasDebugStringAndShortDebugString::value is a compile-time bool constant // that's true if and only if T has methods DebugString() and ShortDebugString() // that return std::string. @@ -954,6 +956,38 @@ typedef char IsNotContainer; template IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; } +namespace implementation { + +template +using VoidType = void; + +template class Op, + class... Args> +struct Detector { + using ValueType = std::false_type; + using Type = Default; +}; + +template class Op, class... Args> +struct Detector>, Op, Args...> { + using ValueType = std::true_type; + using Type = Op; +}; + +} // namespace implementation + +struct NotDetected; +template