OR-Tools  9.6
dump_vars.h
Go to the documentation of this file.
1 // Copyright 2010-2022 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 // DUMP_VARS() is a convenience macro for writing objects to text logs. It
15 // prints all of its arguments as key-value pairs.
16 //
17 // Example:
18 // int foo = 42;
19 // vector<int> bar = {1, 2, 3};
20 // // Prints: foo = 42, bar.size() = 3
21 // LOG(INFO) << DUMP_VARS(foo, bar.size());
22 //
23 // DUMP_VARS() produces high quality human-readable output for most types:
24 // builtin types, strings, anything with operator<<.
25 //
26 // ====[ Limitations ]====
27 //
28 // DUMP_VARS() accepts at most 8 arguments.
29 //
30 // Structured bindings require an extra step to make DUMP_VARS print them. They
31 // need to be listed as first argument of DUMP_VARS_WITH_BINDINGS:
32 //
33 // for (const auto& [x, y, z] : Foo()) {
34 // // Would not compile:
35 // // LOG(INFO) << DUMP_VARS(x, *y, f(z), other_var);
36 // LOG(INFO) << DUMP_VARS_WITH_BINDINGS((x, y, z), x, *y, f(z), other_var);
37 // }
38 
39 #ifndef OR_TOOLS_BASE_DUMP_VARS_H_
40 #define OR_TOOLS_BASE_DUMP_VARS_H_
41 
42 #include <ostream>
43 #include <sstream>
44 #include <string>
45 #include <type_traits>
46 #include <utility>
47 #include <vector>
48 
49 #include "absl/container/inlined_vector.h"
50 
51 /* need extra level to force extra eval */
52 #define DUMP_FOR_EACH_N0(F)
53 #define DUMP_FOR_EACH_N1(F, a) F(a)
54 #define DUMP_FOR_EACH_N2(F, a, ...) F(a) DUMP_FOR_EACH_N1(F, __VA_ARGS__)
55 #define DUMP_FOR_EACH_N3(F, a, ...) F(a) DUMP_FOR_EACH_N2(F, __VA_ARGS__)
56 #define DUMP_FOR_EACH_N4(F, a, ...) F(a) DUMP_FOR_EACH_N3(F, __VA_ARGS__)
57 #define DUMP_FOR_EACH_N5(F, a, ...) F(a) DUMP_FOR_EACH_N4(F, __VA_ARGS__)
58 #define DUMP_FOR_EACH_N6(F, a, ...) F(a) DUMP_FOR_EACH_N5(F, __VA_ARGS__)
59 #define DUMP_FOR_EACH_N7(F, a, ...) F(a) DUMP_FOR_EACH_N6(F, __VA_ARGS__)
60 #define DUMP_FOR_EACH_N8(F, a, ...) F(a) DUMP_FOR_EACH_N7(F, __VA_ARGS__)
61 
62 #define DUMP_CONCATENATE(x, y) x##y
63 #define DUMP_FOR_EACH_(N, F, ...) \
64  DUMP_CONCATENATE(DUMP_FOR_EACH_N, N)(F __VA_OPT__(, __VA_ARGS__))
65 
66 #define DUMP_NARG(...) DUMP_NARG_(__VA_OPT__(__VA_ARGS__, ) DUMP_RSEQ_N())
67 #define DUMP_NARG_(...) DUMP_ARG_N(__VA_ARGS__)
68 #define DUMP_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
69 #define DUMP_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0
70 #define DUMP_FOR_EACH(F, ...) \
71  DUMP_FOR_EACH_(DUMP_NARG(__VA_ARGS__), F __VA_OPT__(, __VA_ARGS__))
72 
73 #define DUMP_VARS(...) DUMP_VARS_WITH_BINDINGS((), __VA_ARGS__)
74 
75 /* need extra level to force extra eval */
76 #define DUMP_STRINGIZE(a) #a,
77 #define DUMP_STRINGIFY(...) DUMP_FOR_EACH(DUMP_STRINGIZE, __VA_ARGS__)
78 
79 // Returns the arguments.
80 #define DUMP_IDENTITY(...) __VA_ARGS__
81 // Removes parenthesis. Requires argument enclosed in parenthesis.
82 #define DUMP_RM_PARENS(...) DUMP_IDENTITY __VA_ARGS__
83 
84 #define DUMP_GEN_ONE_BINDING(a) , &a = a
85 #define DUMP_GEN_BINDING(binding) \
86  &DUMP_FOR_EACH(DUMP_GEN_ONE_BINDING, DUMP_RM_PARENS(binding))
87 
88 #define DUMP_VARS_WITH_BINDINGS(binding, ...) \
89  ::operations_research::base::internal_dump_vars::make_dump_vars<>( \
90  ::operations_research::base::internal_dump_vars::DumpNames{ \
91  DUMP_STRINGIFY(__VA_ARGS__)}, \
92  [DUMP_GEN_BINDING(binding)]( \
93  ::std::ostream& os, const ::std::string& field_sep, \
94  const ::std::string& kv_sep, \
95  const ::operations_research::base::internal_dump_vars::DumpNames& \
96  names) { \
97  ::operations_research::base::internal_dump_vars::print_fields{ \
98  .os = os, \
99  .field_sep = field_sep, \
100  .kv_sep = kv_sep, \
101  .names = names, \
102  }(__VA_ARGS__); \
103  })
104 
106 namespace internal_dump_vars {
107 
108 // needed by routing
109 template <typename T>
110 std::ostream& operator<<(std::ostream& os,
111  const ::absl::InlinedVector<T, 8>& vec) {
112  for (T it : vec) {
113  os << ::std::to_string(it) << ',';
114  }
115  return os;
116 }
117 
118 using DumpNames = ::std::vector<::std::string>;
119 
120 struct print_fields {
121  void operator()() {}
122 
123  template <class T>
124  void operator()(const T& t) {
125  os << names[n++] << kv_sep << t;
126  }
127 
128  template <class T1, class T2, class... Ts>
129  void operator()(const T1& t1, const T2& t2, const Ts&... ts) {
130  os << names[n++] << kv_sep << t1 << field_sep;
131  (*this)(t2, ts...);
132  }
133 
134  std::ostream& os;
135  const ::std::string& field_sep;
136  const ::std::string& kv_sep;
137  const DumpNames& names;
138  ::std::size_t n = 0;
139 };
140 
141 template <class F>
142 class Dump {
143  public:
144  explicit Dump(const ::std::string&& field_sep, const ::std::string&& kv_sep,
145  DumpNames&& names, F f)
146  : field_sep_(::std::move(field_sep)),
147  kv_sep_(::std::move(kv_sep)),
148  names_(::std::move(names)),
149  f_(::std::move(f)) {}
150 
151  ::std::string str() const {
152  ::std::ostringstream oss;
153  oss << *this;
154  return oss.str();
155  }
156 
157  template <class... N>
158  Dump<F> as(N&&... names) const {
159  return Dump<F>(::std::string{field_sep_}, ::std::string{kv_sep_},
160  DumpNames{names...}, f_);
161  }
162 
163  Dump& sep(::std::string&& field_sep) {
164  field_sep_ = ::std::move(field_sep);
165  return *this;
166  }
167 
168  Dump& sep(::std::string&& field_sep, ::std::string&& kv_sep) {
169  field_sep_ = ::std::move(field_sep);
170  kv_sep_ = ::std::move(kv_sep);
171  return *this;
172  }
173 
174  friend ::std::ostream& operator<<(::std::ostream& os, const Dump& dump) {
175  dump.print_fields_(os);
176  return os;
177  }
178 
179  private:
180  void print_fields_(::std::ostream& os) const {
181  f_(os, field_sep_, kv_sep_, names_);
182  }
183 
184  ::std::string field_sep_;
185  ::std::string kv_sep_;
186  DumpNames names_;
187  F f_;
188 };
189 
190 template <class F>
192  return Dump<F>(
193  /*field_sep=*/", ",
194  /*kv_sep=*/" = ", ::std::move(names), ::std::move(f));
195 }
196 
197 } // namespace internal_dump_vars
198 } // namespace operations_research::base
199 
200 #endif // OR_TOOLS_BASE_DUMP_VARS_H_
Dump & sep(::std::string &&field_sep, ::std::string &&kv_sep)
Definition: dump_vars.h:168
Dump(const ::std::string &&field_sep, const ::std::string &&kv_sep, DumpNames &&names, F f)
Definition: dump_vars.h:144
friend ::std::ostream & operator<<(::std::ostream &os, const Dump &dump)
Definition: dump_vars.h:174
Dump & sep(::std::string &&field_sep)
Definition: dump_vars.h:163
Dump< F > make_dump_vars(DumpNames &&names, F f)
Definition: dump_vars.h:191
::std::vector<::std::string > DumpNames
Definition: dump_vars.h:118
std::ostream & operator<<(std::ostream &os, const ::absl::InlinedVector< T, 8 > &vec)
Definition: dump_vars.h:110
void operator()(const T1 &t1, const T2 &t2, const Ts &... ts)
Definition: dump_vars.h:129