Allow CHECK_<OP> variants to be used with unprintable types.

PiperOrigin-RevId: 786406173
Change-Id: Ifc362a702f82a6b3ad33210980dda6f40f14fca4
This commit is contained in:
Abseil Team
2025-07-23 13:36:17 -07:00
committed by Copybara-Service
parent 63d67e9593
commit 57bc7edd87
3 changed files with 44 additions and 0 deletions

View File

@@ -265,6 +265,26 @@ TEST(CHECKTest, TestBinaryChecksWithNullptr) {
ABSL_TEST_CHECK_NE(nullptr, p_not_null);
}
struct ExampleTypeThatHasNoStreamOperator {
bool x;
bool operator==(const ExampleTypeThatHasNoStreamOperator& other) const {
return x == other.x;
}
bool operator==(const bool& other) const { return x == other; }
};
TEST(CHECKDeathTest, TestBinaryChecksWithUnprintable) {
ExampleTypeThatHasNoStreamOperator a{true};
ExampleTypeThatHasNoStreamOperator b{false};
ABSL_TEST_CHECK_EQ(a, a);
EXPECT_DEATH(ABSL_TEST_CHECK_EQ(a, b),
"Check failed: a == b \\(UNPRINTABLE vs. UNPRINTABLE\\)");
ABSL_TEST_CHECK_EQ(a, true);
EXPECT_DEATH(ABSL_TEST_CHECK_EQ(a, false),
"Check failed: a == false \\(UNPRINTABLE vs. 0\\)");
}
#if GTEST_HAS_DEATH_TEST
// Test logging of various char-typed values by failing CHECK*().

View File

@@ -101,6 +101,8 @@ void MakeCheckOpValueString(std::ostream& os, const void* p) {
}
}
void MakeCheckOpUnprintableString(std::ostream& os) { os << "UNPRINTABLE"; }
// Helper functions for string comparisons.
#define DEFINE_CHECK_STROP_IMPL(name, func, expected) \
const char* absl_nullable Check##func##expected##Impl( \

View File

@@ -224,6 +224,19 @@ void MakeCheckOpValueString(std::ostream& os, signed char v);
void MakeCheckOpValueString(std::ostream& os, unsigned char v);
void MakeCheckOpValueString(std::ostream& os, const void* absl_nullable p);
void MakeCheckOpUnprintableString(std::ostream& os);
// A wrapper for types that have no operator<<.
struct UnprintableWrapper {
template <typename T>
explicit UnprintableWrapper(const T&) {}
friend std::ostream& operator<<(std::ostream& os, const UnprintableWrapper&) {
MakeCheckOpUnprintableString(os);
return os;
}
};
namespace detect_specialization {
// MakeCheckOpString is being specialized for every T and U pair that is being
@@ -353,6 +366,15 @@ struct is_streamable<T, std::void_t<decltype(std::declval<std::ostream&>()
<< std::declval<T>())>>
: std::true_type {};
// This overload triggers when T is neither possible to print nor an enum.
template <typename T>
std::enable_if_t<std::negation_v<std::disjunction<
std::is_convertible<T, int>, std::is_enum<T>,
std::is_pointer<T>, std::is_same<T, std::nullptr_t>,
is_streamable<T>, HasAbslStringify<T>>>,
UnprintableWrapper>
Detect(...);
// This overload triggers when T is a scoped enum that has not defined an output
// stream operator (operator<<) or AbslStringify. It causes the enum value to be
// converted to a type that can be streamed. For consistency with other enums, a