1 // Copyright 2024 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef GRPC_SRC_CORE_UTIL_DUMP_ARGS_H
16 #define GRPC_SRC_CORE_UTIL_DUMP_ARGS_H
17
18 #include <ostream>
19 #include <vector>
20
21 #include "absl/functional/any_invocable.h"
22 #include "absl/strings/str_cat.h"
23 #include "absl/strings/str_format.h"
24 #include "absl/strings/string_view.h"
25
26 namespace grpc_core {
27 namespace dump_args_detail {
28
29 // Helper function... just ignore the initializer list passed into it.
30 // Allows doing 'statements' via parameter pack expansion in C++11 - given
31 // template <typename... Ts>:
32 // do_these_things({foo<Ts>()...});
33 // will execute foo<T>() for each T in Ts.
34 template <typename T>
do_these_things(std::initializer_list<T>)35 void do_these_things(std::initializer_list<T>) {}
36
37 class DumpArgs {
38 public:
39 template <typename... Args>
DumpArgs(const char * arg_string,const Args &...args)40 explicit DumpArgs(const char* arg_string, const Args&... args)
41 : arg_string_(arg_string) {
42 do_these_things({AddDumper(&args)...});
43 }
44
45 template <typename Sink>
AbslStringify(Sink & sink,const DumpArgs & dumper)46 friend void AbslStringify(Sink& sink, const DumpArgs& dumper) {
47 CustomSinkImpl<Sink> custom_sink(sink);
48 dumper.Stringify(custom_sink);
49 }
50
51 friend std::ostream& operator<<(std::ostream& out, const DumpArgs& dumper) {
52 return out << absl::StrCat(dumper);
53 }
54
55 private:
56 class CustomSink {
57 public:
58 virtual void Append(absl::string_view x) = 0;
59
60 protected:
61 ~CustomSink() = default;
62 };
63
64 template <typename Sink>
65 class CustomSinkImpl final : public CustomSink {
66 public:
CustomSinkImpl(Sink & sink)67 explicit CustomSinkImpl(Sink& sink) : sink_(sink) {}
Append(absl::string_view x)68 void Append(absl::string_view x) override { sink_.Append(x); }
69
70 private:
71 Sink& sink_;
72 };
73
74 template <typename T>
AddDumper(T * p)75 int AddDumper(T* p) {
76 arg_dumpers_.push_back(
77 [p](CustomSink& os) { os.Append(absl::StrCat(*p)); });
78 return 0;
79 }
80
AddDumper(void const * const * p)81 int AddDumper(void const* const* p) {
82 arg_dumpers_.push_back(
83 [p](CustomSink& os) { os.Append(absl::StrFormat("%p", *p)); });
84 return 0;
85 }
86
87 template <typename T>
AddDumper(T const * const * p)88 int AddDumper(T const* const* p) {
89 return AddDumper(reinterpret_cast<void const* const*>(p));
90 }
91
92 template <typename T>
AddDumper(T * const * p)93 int AddDumper(T* const* p) {
94 return AddDumper(const_cast<T const* const*>(p));
95 }
96
97 template <typename T>
AddDumper(T const ** p)98 int AddDumper(T const** p) {
99 return AddDumper(const_cast<T const* const*>(p));
100 }
101
102 void Stringify(CustomSink& sink) const;
103
104 const char* arg_string_;
105 std::vector<absl::AnyInvocable<void(CustomSink&) const>> arg_dumpers_;
106 };
107
108 } // namespace dump_args_detail
109 } // namespace grpc_core
110
111 // Helper to print a list of variables and their values.
112 // Each type must be streamable to std::ostream.
113 // Usage:
114 // int a = 1;
115 // int b = 2;
116 // LOG(INFO) << GRPC_DUMP_ARGS(a, b)
117 // Output:
118 // a = 1, b = 2
119 #define GRPC_DUMP_ARGS(...) \
120 grpc_core::dump_args_detail::DumpArgs(#__VA_ARGS__, __VA_ARGS__)
121
122 #endif // GRPC_SRC_CORE_UTIL_DUMP_ARGS_H
123