diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index e1282d7293..fc2fc093cb 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -441,6 +441,7 @@ rtc_library("logging") { "synchronization:mutex", "//third_party/abseil-cpp/absl/base:core_headers", "//third_party/abseil-cpp/absl/meta:type_traits", + "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -489,6 +490,7 @@ rtc_library("checks") { "system:inline", "system:rtc_export", "//third_party/abseil-cpp/absl/meta:type_traits", + "//third_party/abseil-cpp/absl/strings", "//third_party/abseil-cpp/absl/strings:string_view", ] if (build_with_chromium) { diff --git a/rtc_base/DEPS b/rtc_base/DEPS index 17d6cf6427..b5aacb156c 100644 --- a/rtc_base/DEPS +++ b/rtc_base/DEPS @@ -6,6 +6,10 @@ include_rules = [ ] specific_include_rules = { + "checks.h": [ + "+absl/strings/has_absl_stringify.h", + "+absl/strings/str_cat.h", + ], "protobuf_utils.h": [ "+third_party/protobuf", ], @@ -18,6 +22,10 @@ specific_include_rules = { "event_tracer\.cc": [ "+third_party/perfetto", ], + "logging.h": [ + "+absl/strings/has_absl_stringify.h", + "+absl/strings/str_cat.h", + ], "trace_event\.h": [ "+third_party/perfetto", ], diff --git a/rtc_base/checks.h b/rtc_base/checks.h index 99fee97d0a..130b8b9259 100644 --- a/rtc_base/checks.h +++ b/rtc_base/checks.h @@ -55,6 +55,8 @@ RTC_NORETURN void rtc_FatalMessage(const char* file, int line, const char* msg); #include #include "absl/meta/type_traits.h" +#include "absl/strings/has_absl_stringify.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "api/scoped_refptr.h" #include "rtc_base/numerics/safe_compare.h" @@ -220,6 +222,12 @@ ToStringVal MakeVal(const T& x) { return {ToLogString(x)}; } +template ::value>* = nullptr> +ToStringVal MakeVal(const T& x) { + return {absl::StrCat(x)}; +} + // Ephemeral type that represents the result of the logging << operator. template class LogStreamer; diff --git a/rtc_base/checks_unittest.cc b/rtc_base/checks_unittest.cc index 95deba9f1c..4a3dfa422b 100644 --- a/rtc_base/checks_unittest.cc +++ b/rtc_base/checks_unittest.cc @@ -10,8 +10,14 @@ #include "rtc_base/checks.h" +#include "test/gmock.h" #include "test/gtest.h" +namespace { + +using ::testing::HasSubstr; +using ::testing::Not; + TEST(ChecksTest, ExpressionNotEvaluatedWhenCheckPassing) { int i = 0; RTC_CHECK(true) << "i=" << ++i; @@ -19,6 +25,14 @@ TEST(ChecksTest, ExpressionNotEvaluatedWhenCheckPassing) { } #if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) + +struct StructWithStringfy { + template + friend void AbslStringify(Sink& sink, const StructWithStringfy& /*self*/) { + sink.Append("absl-stringify"); + } +}; + TEST(ChecksDeathTest, Checks) { #if RTC_CHECK_MSG_ENABLED EXPECT_DEATH(RTC_FATAL() << "message", @@ -44,6 +58,9 @@ TEST(ChecksDeathTest, Checks) { "# last system error: \\w+\n" "# Check failed: false\n" "# Hi there!"); + + StructWithStringfy t; + EXPECT_DEATH(RTC_CHECK(false) << t, HasSubstr("absl-stringify")); #else EXPECT_DEATH(RTC_FATAL() << "message", "\n\n#\n" @@ -68,6 +85,12 @@ TEST(ChecksDeathTest, Checks) { "# last system error: \\w+\n" "# Check failed.\n" "# "); + + // Should compile, but shouldn't try to stringify 't' + StructWithStringfy t; + EXPECT_DEATH(RTC_CHECK(false) << t, Not(HasSubstr("absl-stringify"))); #endif // RTC_CHECK_MSG_ENABLED } #endif // GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) + +} // namespace diff --git a/rtc_base/logging.h b/rtc_base/logging.h index 165a83b467..acff13ba12 100644 --- a/rtc_base/logging.h +++ b/rtc_base/logging.h @@ -59,6 +59,8 @@ #include "absl/base/attributes.h" #include "absl/meta/type_traits.h" +#include "absl/strings/has_absl_stringify.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "api/units/timestamp.h" #include "rtc_base/platform_thread_types.h" @@ -340,20 +342,26 @@ ToStringVal MakeVal(const T& x) { return {ToLogString(x)}; } +template ::value>* = nullptr> +ToStringVal MakeVal(const T& x) { + return {absl::StrCat(x)}; +} + // Handle arbitrary types other than the above by falling back to stringstream. // TODO(bugs.webrtc.org/9278): Get rid of this overload when callers don't need // it anymore. No in-tree caller does, but some external callers still do. -template < - typename T, - typename T1 = absl::decay_t, - absl::enable_if_t::value && - !std::is_same::value && - !std::is_same::value && - !has_to_log_string::value && +template , + std::enable_if_t::value && // + !std::is_same::value && // + !std::is_same::value && // + !has_to_log_string::value && // + !absl::HasAbslStringify::value && #ifdef WEBRTC_ANDROID - !std::is_same::value && + !std::is_same::value && // #endif - !std::is_same::value>* = nullptr> + !std::is_same::value>* = nullptr> ToStringVal MakeVal(const T& x) { std::ostringstream os; // no-presubmit-check TODO(webrtc:8982) os << x; diff --git a/rtc_base/logging_unittest.cc b/rtc_base/logging_unittest.cc index b05907e74a..9e6f2976a0 100644 --- a/rtc_base/logging_unittest.cc +++ b/rtc_base/logging_unittest.cc @@ -29,6 +29,8 @@ namespace rtc { namespace { +using ::testing::HasSubstr; + #if defined(WEBRTC_WIN) constexpr char kFakeFilePath[] = "some\\path\\myfile.cc"; #else @@ -367,6 +369,26 @@ TEST(LogTest, NoopSeverityDoesNotRunStringFormatting) { EXPECT_FALSE(was_called); } +struct StructWithStringfy { + template + friend void AbslStringify(Sink& sink, const StructWithStringfy& /*self*/) { + sink.Append("absl-stringify"); + } +}; + +TEST(LogTest, UseAbslStringForCustomTypes) { + std::string str; + LogSinkImpl stream(&str); + LogMessage::AddLogToStream(&stream, LS_INFO); + StructWithStringfy t; + + RTC_LOG(LS_INFO) << t; + + EXPECT_THAT(str, HasSubstr("absl-stringify")); + + LogMessage::RemoveLogToStream(&stream); +} + struct TestStruct {}; std::string ToLogString(TestStruct foo) { return "bar";