Allow CHECK_<OP> variants to be used with unprintable types.
PiperOrigin-RevId: 786406173
Change-Id: Ifc362a702f82a6b3ad33210980dda6f40f14fca4
diff --git a/absl/log/check_test_impl.inc b/absl/log/check_test_impl.inc
index 37226a3..5a7caf4 100644
--- a/absl/log/check_test_impl.inc
+++ b/absl/log/check_test_impl.inc
@@ -265,6 +265,26 @@
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*().
diff --git a/absl/log/internal/check_op.cc b/absl/log/internal/check_op.cc
index 23db63b..5db98dd 100644
--- a/absl/log/internal/check_op.cc
+++ b/absl/log/internal/check_op.cc
@@ -101,6 +101,8 @@
}
}
+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( \
diff --git a/absl/log/internal/check_op.h b/absl/log/internal/check_op.h
index d7b55f6..4554475 100644
--- a/absl/log/internal/check_op.h
+++ b/absl/log/internal/check_op.h
@@ -224,6 +224,19 @@
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 @@
<< 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