diff --git a/CMakeLists.txt b/CMakeLists.txt index ce144429c55..d9360e09e50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13057,6 +13057,7 @@ target_link_libraries(dump_args_test gtest absl::any_invocable absl::check + absl::str_format ) diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index 1ebb477b21d..de10143e6fa 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -9556,6 +9556,7 @@ targets: - gtest - absl/functional:any_invocable - absl/log:check + - absl/strings:str_format uses_polling: false - name: duplicate_header_bad_client_test gtest: true diff --git a/include/grpc/event_engine/event_engine.h b/include/grpc/event_engine/event_engine.h index add6593aa7b..21a0c1c4826 100644 --- a/include/grpc/event_engine/event_engine.h +++ b/include/grpc/event_engine/event_engine.h @@ -503,6 +503,20 @@ bool operator!=(const EventEngine::ConnectionHandle& lhs, std::ostream& operator<<(std::ostream& out, const EventEngine::ConnectionHandle& handle); +namespace detail { +std::string FormatHandleString(uint64_t key1, uint64_t key2); +} + +template +void AbslStringify(Sink& out, const EventEngine::ConnectionHandle& handle) { + out.Append(detail::FormatHandleString(handle.keys[0], handle.keys[1])); +} + +template +void AbslStringify(Sink& out, const EventEngine::TaskHandle& handle) { + out.Append(detail::FormatHandleString(handle.keys[0], handle.keys[1])); +} + } // namespace experimental } // namespace grpc_event_engine diff --git a/src/core/BUILD b/src/core/BUILD index c3366364c39..fc6a9b8f7fb 100644 --- a/src/core/BUILD +++ b/src/core/BUILD @@ -60,6 +60,7 @@ grpc_cc_library( "absl/functional:any_invocable", "absl/log:check", "absl/strings", + "absl/strings:str_format", ], language = "c++", deps = [ diff --git a/src/core/lib/event_engine/event_engine.cc b/src/core/lib/event_engine/event_engine.cc index 74f24fdc9ce..5a162f871a1 100644 --- a/src/core/lib/event_engine/event_engine.cc +++ b/src/core/lib/event_engine/event_engine.cc @@ -23,6 +23,13 @@ const EventEngine::TaskHandle EventEngine::TaskHandle::kInvalid = {-1, -1}; const EventEngine::ConnectionHandle EventEngine::ConnectionHandle::kInvalid = { -1, -1}; +namespace detail { +std::string FormatHandleString(uint64_t key1, uint64_t key2) { + return absl::StrCat("{", absl::Hex(key1, absl::kZeroPad16), ",", + absl::Hex(key2, absl::kZeroPad16), "}"); +} +} // namespace detail + namespace { template bool eq(const T& lhs, const T& rhs) { @@ -30,8 +37,7 @@ bool eq(const T& lhs, const T& rhs) { } template std::ostream& printout(std::ostream& out, const T& handle) { - out << absl::StrCat("{", absl::Hex(handle.keys[0], absl::kZeroPad16), ",", - absl::Hex(handle.keys[1], absl::kZeroPad16), "}"); + out << detail::FormatHandleString(handle.keys[0], handle.keys[1]); return out; } } // namespace diff --git a/src/core/lib/gprpp/dump_args.cc b/src/core/lib/gprpp/dump_args.cc index d4400bbf296..1e2972f6dd7 100644 --- a/src/core/lib/gprpp/dump_args.cc +++ b/src/core/lib/gprpp/dump_args.cc @@ -21,7 +21,7 @@ namespace grpc_core { namespace dump_args_detail { -std::ostream& operator<<(std::ostream& out, const DumpArgs& args) { +void DumpArgs::Stringify(CustomSink& sink) const { // Parse the argument string into a vector of keys. // #__VA_ARGS__ produces a stringified version of the arguments passed to the // macro. It's comma separated, and we can use that to split the string into @@ -29,8 +29,8 @@ std::ostream& operator<<(std::ostream& out, const DumpArgs& args) { // we need to skip commas that are inside parenthesis. std::vector keys; int depth = 0; - const char* start = args.arg_string_; - for (const char* p = args.arg_string_; *p; ++p) { + const char* start = arg_string_; + for (const char* p = arg_string_; *p; ++p) { if (*p == '(') { ++depth; } else if (*p == ')') { @@ -41,13 +41,13 @@ std::ostream& operator<<(std::ostream& out, const DumpArgs& args) { } } keys.push_back(start); - CHECK_EQ(keys.size(), args.arg_dumpers_.size()); + CHECK_EQ(keys.size(), arg_dumpers_.size()); for (size_t i = 0; i < keys.size(); i++) { - if (i != 0) out << ", "; - out << absl::StripAsciiWhitespace(keys[i]) << " = "; - args.arg_dumpers_[i](out); + if (i != 0) sink.Append(", "); + sink.Append(absl::StripAsciiWhitespace(keys[i])); + sink.Append(" = "); + arg_dumpers_[i](sink); } - return out; } } // namespace dump_args_detail diff --git a/src/core/lib/gprpp/dump_args.h b/src/core/lib/gprpp/dump_args.h index abd5b6a2027..1e89fa48c65 100644 --- a/src/core/lib/gprpp/dump_args.h +++ b/src/core/lib/gprpp/dump_args.h @@ -19,6 +19,9 @@ #include #include "absl/functional/any_invocable.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" namespace grpc_core { namespace dump_args_detail { @@ -39,17 +42,62 @@ class DumpArgs { do_these_things({AddDumper(&args)...}); } - friend std::ostream& operator<<(std::ostream& out, const DumpArgs& args); + template + friend void AbslStringify(Sink& sink, const DumpArgs& dumper) { + CustomSinkImpl custom_sink(sink); + dumper.Stringify(custom_sink); + } + + friend std::ostream& operator<<(std::ostream& out, const DumpArgs& dumper) { + return out << absl::StrCat(dumper); + } private: + class CustomSink { + public: + virtual void Append(absl::string_view x) = 0; + + protected: + ~CustomSink() = default; + }; + + template + class CustomSinkImpl final : public CustomSink { + public: + explicit CustomSinkImpl(Sink& sink) : sink_(sink) {} + void Append(absl::string_view x) override { sink_.Append(x); } + + private: + Sink& sink_; + }; + template int AddDumper(T* p) { - arg_dumpers_.push_back([p](std::ostream& os) { os << *p; }); + arg_dumpers_.push_back( + [p](CustomSink& os) { os.Append(absl::StrCat(*p)); }); return 0; } + int AddDumper(void** p) { + arg_dumpers_.push_back( + [p](CustomSink& os) { os.Append(absl::StrFormat("%p", *p)); }); + return 0; + } + + template + int AddDumper(T** p) { + return AddDumper(reinterpret_cast(p)); + } + + template + int AddDumper(T* const* p) { + return AddDumper(const_cast(p)); + } + + void Stringify(CustomSink& sink) const; + const char* arg_string_; - std::vector> arg_dumpers_; + std::vector> arg_dumpers_; }; } // namespace dump_args_detail diff --git a/src/core/lib/gprpp/time.h b/src/core/lib/gprpp/time.h index 1f2a4815682..a6c48cdb1a8 100644 --- a/src/core/lib/gprpp/time.h +++ b/src/core/lib/gprpp/time.h @@ -169,6 +169,11 @@ class Timestamp { std::string ToString() const; + template + friend void AbslStringify(Sink& sink, const Timestamp& t) { + sink.Append(t.ToString()); + } + private: explicit constexpr Timestamp(int64_t millis) : millis_(millis) {} @@ -293,6 +298,11 @@ class Duration { // https://developers.google.com/protocol-buffers/docs/proto3#json std::string ToJsonString() const; + template + friend void AbslStringify(Sink& sink, const Duration& t) { + sink.Append(t.ToString()); + } + private: explicit constexpr Duration(int64_t millis) : millis_(millis) {} @@ -377,9 +387,6 @@ inline Timestamp& Timestamp::operator+=(Duration duration) { void TestOnlySetProcessEpoch(gpr_timespec epoch); -std::ostream& operator<<(std::ostream& out, Timestamp timestamp); -std::ostream& operator<<(std::ostream& out, Duration duration); - } // namespace grpc_core #endif // GRPC_SRC_CORE_LIB_GPRPP_TIME_H diff --git a/test/core/event_engine/handle_tests.cc b/test/core/event_engine/handle_tests.cc index 418dcb40dd1..9e082c23a0e 100644 --- a/test/core/event_engine/handle_tests.cc +++ b/test/core/event_engine/handle_tests.cc @@ -13,6 +13,7 @@ // limitations under the License. #include +#include "absl/strings/str_cat.h" #include "gtest/gtest.h" #include @@ -46,6 +47,11 @@ TYPED_TEST(TaskHandleTest, Validity) { ASSERT_EQ(TypeParam::kInvalid, TypeParam::kInvalid); } +TYPED_TEST(TaskHandleTest, AbslStringify) { + TypeParam t{42, 43}; + ASSERT_EQ(absl::StrCat(t), "{000000000000002a,000000000000002b}"); +} + int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/test/core/gprpp/dump_args_test.cc b/test/core/gprpp/dump_args_test.cc index 2207d4eab62..09c6fafc387 100644 --- a/test/core/gprpp/dump_args_test.cc +++ b/test/core/gprpp/dump_args_test.cc @@ -18,24 +18,17 @@ #include "gtest/gtest.h" -template -std::string Stringify(const T& t) { - std::ostringstream oss; - oss << t; - return oss.str(); -} - int add(int a, int b) { return a + b; } TEST(DumpArgsTest, Basic) { int a = 1; int b = 2; int c = 3; - EXPECT_EQ("a = 1, b = 2, c = 3", Stringify(GRPC_DUMP_ARGS(a, b, c))); + EXPECT_EQ("a = 1, b = 2, c = 3", absl::StrCat(GRPC_DUMP_ARGS(a, b, c))); } TEST(DumpArgsTest, FunctionCall) { - EXPECT_EQ("add(1, 2) = 3", Stringify(GRPC_DUMP_ARGS(add(1, 2)))); + EXPECT_EQ("add(1, 2) = 3", absl::StrCat(GRPC_DUMP_ARGS(add(1, 2)))); } int main(int argc, char** argv) {