1 // Copyright 2019 The SwiftShader Authors. All Rights Reserved.
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 rr_Print_hpp
16 #define rr_Print_hpp
17
18 #ifdef ENABLE_RR_PRINT
19
20 # include "Reactor.hpp"
21 # include "SIMD.hpp"
22
23 # include <string>
24 # include <vector>
25
26 namespace rr {
27
28 // PrintValue holds the printf format and value(s) for a single argument
29 // to Print(). A single argument can be expanded into multiple printf
30 // values - for example a Float4 will expand to "%f %f %f %f" and four
31 // scalar values.
32 // The PrintValue constructor accepts the following:
33 // * Reactor LValues, RValues, Pointers.
34 // * Standard Plain-Old-Value types (int, float, bool, etc)
35 // * Custom types that specialize the PrintValue::Ty template struct.
36 // * Static arrays in the form T[N] where T can be any of the above.
37 class PrintValue
38 {
39 public:
40 // Ty is a template that can be specialized for printing type T.
41 // Each specialization must expose:
42 // * A 'static std::string fmt(const T& v)' method that provides the
43 // printf format specifier.
44 // * A 'static std::vector<rr::Value*> val(const T& v)' method that
45 // returns all the printf format values.
46 template<typename T>
47 struct Ty
48 {
49 // static std::string fmt(const T& v);
50 // static std::vector<rr::Value*> val(const T& v);
51 };
52
53 // returns the printf values for all the values in the given array.
54 template<typename T>
val(const T * list,int count)55 static std::vector<Value *> val(const T *list, int count)
56 {
57 std::vector<Value *> values;
58 values.reserve(count);
59 for(int i = 0; i < count; i++)
60 {
61 auto v = val(list[i]);
62 values.insert(values.end(), v.begin(), v.end());
63 }
64 return values;
65 }
66
67 // fmt returns the comma-delimited list of printf format strings for
68 // every element in the provided list, all enclosed in square brackets.
69 template<typename T>
fmt(const T * list,int count)70 static std::string fmt(const T *list, int count)
71 {
72 std::string out = "[";
73 for(int i = 0; i < count; i++)
74 {
75 if(i > 0) { out += ", "; }
76 out += fmt(list[i]);
77 }
78 return out + "]";
79 }
80
addr(const void * ptr)81 static std::string addr(const void *ptr)
82 {
83 char buf[32];
84 snprintf(buf, sizeof(buf), "%p", ptr);
85 return buf;
86 }
87
88 const std::string format;
89 const std::vector<Value *> values;
90
91 // Constructs a PrintValue for the given value.
92 template<typename T>
PrintValue(const T & v)93 PrintValue(const T &v)
94 : format(fmt(v))
95 , values(val(v))
96 {}
97
98 // Constructs a PrintValue for the given static array.
99 template<typename T, int N>
PrintValue(const T (& v)[N])100 PrintValue(const T (&v)[N])
101 : format(fmt(&v[0], N))
102 , values(val(&v[0], N))
103 {}
104
105 // Constructs a PrintValue for the given array starting at arr of length
106 // len.
107 template<typename T>
PrintValue(const T * arr,int len)108 PrintValue(const T *arr, int len)
109 : format(fmt(arr, len))
110 , values(val(arr, len))
111 {}
112
113 // PrintValue constructors for plain-old-data values.
PrintValue(bool v)114 PrintValue(bool v)
115 : format(v ? "true" : "false")
116 {}
PrintValue(int8_t v)117 PrintValue(int8_t v)
118 : format(std::to_string(v))
119 {}
PrintValue(uint8_t v)120 PrintValue(uint8_t v)
121 : format(std::to_string(v))
122 {}
PrintValue(int16_t v)123 PrintValue(int16_t v)
124 : format(std::to_string(v))
125 {}
PrintValue(uint16_t v)126 PrintValue(uint16_t v)
127 : format(std::to_string(v))
128 {}
PrintValue(int32_t v)129 PrintValue(int32_t v)
130 : format(std::to_string(v))
131 {}
PrintValue(uint32_t v)132 PrintValue(uint32_t v)
133 : format(std::to_string(v))
134 {}
PrintValue(int64_t v)135 PrintValue(int64_t v)
136 : format(std::to_string(v))
137 {}
PrintValue(uint64_t v)138 PrintValue(uint64_t v)
139 : format(std::to_string(v))
140 {}
PrintValue(float v)141 PrintValue(float v)
142 : format(std::to_string(v))
143 {}
PrintValue(double v)144 PrintValue(double v)
145 : format(std::to_string(v))
146 {}
PrintValue(const char * v)147 PrintValue(const char *v)
148 : format(v)
149 {}
150
151 template<typename T>
PrintValue(T * v)152 PrintValue(T *v)
153 : format(addr(v))
154 {}
155
156 // vals is a helper to build composite value lists.
157 // vals returns the full, sequential list of printf argument values used
158 // to print all the provided variadic values.
159 // vals() is intended to be used by implementations of
160 // PrintValue::Ty<>::vals() to help declare aggregate types.
161 // For example, if you were declaring a PrintValue::Ty<> specialization
162 // for a custom Mat4x4 matrix formed from four Vector4 values, you'd
163 // write:
164 //
165 // namespace rr
166 // {
167 // template <> struct PrintValue::Ty<Mat4x4>
168 // {
169 // static std::string fmt(const Mat4x4& v)
170 // {
171 // return "[a: <%f, %f, %f, %f>,"
172 // " b: <%f, %f, %f, %f>,"
173 // " c: <%f, %f, %f, %f>,"
174 // " d: <%f, %f, %f, %f>]";
175 // }
176 // static std::vector<rr::Value*> val(const Mat4x4& v)
177 // {
178 // return PrintValue::vals(v.a, v.b, v.c, v.d);
179 // }
180 // };
181 // }
182 template<typename... ARGS>
vals(ARGS...v)183 static std::vector<Value *> vals(ARGS... v)
184 {
185 std::vector<std::vector<Value *>> lists = { val(v)... };
186 std::vector<Value *> joined;
187 for(const auto &list : lists)
188 {
189 joined.insert(joined.end(), list.begin(), list.end());
190 }
191 return joined;
192 }
193
194 // returns the printf format specifier for the given type via the
195 // PrintValue::Ty<T> specialization.
196 template<typename T>
fmt(const T & v)197 static std::string fmt(const T &v)
198 {
199 return Ty<T>::fmt(v);
200 }
201
202 // returns the printf value for the given type with a
203 // PrintValue::Ty<T> specialization.
204 template<typename T>
val(const T & v)205 static std::vector<Value *> val(const T &v)
206 {
207 return Ty<T>::val(v);
208 }
209 };
210
211 // PrintValue::Ty<T> specializations for basic types.
212 template<>
213 struct PrintValue::Ty<const char *>
214 {
fmtrr::PrintValue::Ty215 static std::string fmt(const char *v) { return "%s"; }
216 static std::vector<Value *> val(const char *v);
217 };
218 template<>
219 struct PrintValue::Ty<std::string>
220 {
fmtrr::PrintValue::Ty221 static std::string fmt(const std::string &v) { return PrintValue::Ty<const char *>::fmt(v.c_str()); }
valrr::PrintValue::Ty222 static std::vector<Value *> val(const std::string &v) { return PrintValue::Ty<const char *>::val(v.c_str()); }
223 };
224
225 // PrintValue::Ty<T> specializations for standard Reactor types.
226 template<>
227 struct PrintValue::Ty<Bool>
228 {
fmtrr::PrintValue::Ty229 static std::string fmt(const RValue<Bool> &v) { return "%s"; }
230 static std::vector<Value *> val(const RValue<Bool> &v);
231 };
232 template<>
233 struct PrintValue::Ty<Byte>
234 {
fmtrr::PrintValue::Ty235 static std::string fmt(const RValue<Byte> &v) { return "%d"; }
236 static std::vector<Value *> val(const RValue<Byte> &v);
237 };
238 template<>
239 struct PrintValue::Ty<Byte4>
240 {
fmtrr::PrintValue::Ty241 static std::string fmt(const RValue<Byte4> &v) { return "[%d, %d, %d, %d]"; }
242 static std::vector<Value *> val(const RValue<Byte4> &v);
243 };
244 template<>
245 struct PrintValue::Ty<Int>
246 {
fmtrr::PrintValue::Ty247 static std::string fmt(const RValue<Int> &v) { return "%d"; }
248 static std::vector<Value *> val(const RValue<Int> &v);
249 };
250 template<>
251 struct PrintValue::Ty<Int2>
252 {
fmtrr::PrintValue::Ty253 static std::string fmt(const RValue<Int2> &v) { return "[%d, %d]"; }
254 static std::vector<Value *> val(const RValue<Int2> &v);
255 };
256 template<>
257 struct PrintValue::Ty<Int4>
258 {
fmtrr::PrintValue::Ty259 static std::string fmt(const RValue<Int4> &v) { return "[%d, %d, %d, %d]"; }
260 static std::vector<Value *> val(const RValue<Int4> &v);
261 };
262 template<>
263 struct PrintValue::Ty<UInt>
264 {
fmtrr::PrintValue::Ty265 static std::string fmt(const RValue<UInt> &v) { return "%u"; }
266 static std::vector<Value *> val(const RValue<UInt> &v);
267 };
268 template<>
269 struct PrintValue::Ty<UInt2>
270 {
fmtrr::PrintValue::Ty271 static std::string fmt(const RValue<UInt2> &v) { return "[%u, %u]"; }
272 static std::vector<Value *> val(const RValue<UInt2> &v);
273 };
274 template<>
275 struct PrintValue::Ty<UInt4>
276 {
fmtrr::PrintValue::Ty277 static std::string fmt(const RValue<UInt4> &v) { return "[%u, %u, %u, %u]"; }
278 static std::vector<Value *> val(const RValue<UInt4> &v);
279 };
280 template<>
281 struct PrintValue::Ty<Short>
282 {
fmtrr::PrintValue::Ty283 static std::string fmt(const RValue<Short> &v) { return "%d"; }
284 static std::vector<Value *> val(const RValue<Short> &v);
285 };
286 template<>
287 struct PrintValue::Ty<Short4>
288 {
fmtrr::PrintValue::Ty289 static std::string fmt(const RValue<Short4> &v) { return "[%d, %d, %d, %d]"; }
290 static std::vector<Value *> val(const RValue<Short4> &v);
291 };
292 template<>
293 struct PrintValue::Ty<UShort>
294 {
fmtrr::PrintValue::Ty295 static std::string fmt(const RValue<UShort> &v) { return "%u"; }
296 static std::vector<Value *> val(const RValue<UShort> &v);
297 };
298 template<>
299 struct PrintValue::Ty<UShort4>
300 {
fmtrr::PrintValue::Ty301 static std::string fmt(const RValue<UShort4> &v) { return "[%u, %u, %u, %u]"; }
302 static std::vector<Value *> val(const RValue<UShort4> &v);
303 };
304 template<>
305 struct PrintValue::Ty<Float>
306 {
fmtrr::PrintValue::Ty307 static std::string fmt(const RValue<Float> &v) { return "%f"; }
308 static std::vector<Value *> val(const RValue<Float> &v);
309 };
310 template<>
311 struct PrintValue::Ty<Float4>
312 {
fmtrr::PrintValue::Ty313 static std::string fmt(const RValue<Float4> &v) { return "[%f, %f, %f, %f]"; }
314 static std::vector<Value *> val(const RValue<Float4> &v);
315 };
316 template<>
317 struct PrintValue::Ty<Long>
318 {
fmtrr::PrintValue::Ty319 static std::string fmt(const RValue<Long> &v) { return "%lld"; }
valrr::PrintValue::Ty320 static std::vector<Value *> val(const RValue<Long> &v) { return { v.value() }; }
321 };
322 template<typename T>
323 struct PrintValue::Ty<Pointer<T>>
324 {
fmtrr::PrintValue::Ty325 static std::string fmt(const RValue<Pointer<T>> &v) { return "%p"; }
valrr::PrintValue::Ty326 static std::vector<Value *> val(const RValue<Pointer<T>> &v) { return { v.value() }; }
327 };
328 template<>
329 struct PrintValue::Ty<SIMD::Pointer>
330 {
fmtrr::PrintValue::Ty331 static std::string fmt(const SIMD::Pointer &v)
332 {
333 if(v.isBasePlusOffset)
334 {
335 std::string format;
336 for(int i = 1; i < SIMD::Width; i++) { format += ", %d"; }
337 return "{%p + [%d" + format + "]}";
338 }
339 else
340 {
341 std::string format;
342 for(int i = 1; i < SIMD::Width; i++) { format += ", %p"; }
343 return "{%p" + format + "}";
344 }
345 }
346
valrr::PrintValue::Ty347 static std::vector<Value *> val(const SIMD::Pointer &v)
348 {
349 return v.getPrintValues();
350 }
351 };
352 template<>
353 struct PrintValue::Ty<SIMD::Int>
354 {
fmtrr::PrintValue::Ty355 static std::string fmt(const RValue<SIMD::Int> &v)
356 {
357 std::string format;
358 for(int i = 1; i < SIMD::Width; i++) { format += ", %d"; }
359 return "[%d" + format + "]";
360 }
361 static std::vector<Value *> val(const RValue<SIMD::Int> &v);
362 };
363 template<>
364 struct PrintValue::Ty<SIMD::UInt>
365 {
fmtrr::PrintValue::Ty366 static std::string fmt(const RValue<SIMD::UInt> &v)
367 {
368 std::string format;
369 for(int i = 1; i < SIMD::Width; i++) { format += ", %u"; }
370 return "[%u" + format + "]";
371 }
372 static std::vector<Value *> val(const RValue<SIMD::UInt> &v);
373 };
374 template<>
375 struct PrintValue::Ty<SIMD::Float>
376 {
fmtrr::PrintValue::Ty377 static std::string fmt(const RValue<SIMD::Float> &v)
378 {
379 std::string format;
380 for(int i = 1; i < SIMD::Width; i++) { format += ", %f"; }
381 return "[%f" + format + "]";
382 }
383 static std::vector<Value *> val(const RValue<SIMD::Float> &v);
384 };
385 template<typename T>
386 struct PrintValue::Ty<Reference<T>>
387 {
fmtrr::PrintValue::Ty388 static std::string fmt(const Reference<T> &v) { return PrintValue::Ty<T>::fmt(v); }
valrr::PrintValue::Ty389 static std::vector<Value *> val(const Reference<T> &v) { return PrintValue::Ty<T>::val(v); }
390 };
391 template<typename T>
392 struct PrintValue::Ty<RValue<T>>
393 {
fmtrr::PrintValue::Ty394 static std::string fmt(const RValue<T> &v) { return PrintValue::Ty<T>::fmt(v); }
valrr::PrintValue::Ty395 static std::vector<Value *> val(const RValue<T> &v) { return PrintValue::Ty<T>::val(v); }
396 };
397
398 // VPrintf emits a call to printf() using vals[0] as the format string,
399 // and vals[1..n] as the args.
400 void VPrintf(const std::vector<Value *> &vals);
401
402 // Printv emits a call to printf() using the function, file and line,
403 // message and optional values.
404 // See Printv below.
405 void Printv(const char *function, const char *file, int line, const char *msg, std::initializer_list<PrintValue> vals);
406
407 // Printv emits a call to printf() using the provided message and optional
408 // values.
409 // Printf replaces any bracketed indices in the message with string
410 // representations of the corresponding value in vals.
411 // For example:
412 // Printv("{0} and {1}", "red", "green");
413 // Would print the string:
414 // "red and green"
415 // Arguments can be indexed in any order.
416 // Invalid indices are not substituted.
Printv(const char * msg,std::initializer_list<PrintValue> vals)417 inline void Printv(const char *msg, std::initializer_list<PrintValue> vals)
418 {
419 Printv(nullptr, nullptr, 0, msg, vals);
420 }
421
422 // Print is a wrapper over Printv that wraps the variadic arguments into an
423 // initializer_list before calling Printv.
424 template<typename... ARGS>
Print(const char * msg,const ARGS &...vals)425 void Print(const char *msg, const ARGS &...vals)
426 {
427 Printv(msg, { vals... });
428 }
429
430 // Print is a wrapper over Printv that wraps the variadic arguments into an
431 // initializer_list before calling Printv.
432 template<typename... ARGS>
Print(const char * function,const char * file,int line,const char * msg,const ARGS &...vals)433 void Print(const char *function, const char *file, int line, const char *msg, const ARGS &...vals)
434 {
435 Printv(function, file, line, msg, { vals... });
436 }
437
438 // RR_LOG is a macro that calls Print(), automatically populating the
439 // function, file and line parameters and appending a newline to the string.
440 //
441 // RR_LOG() is intended to be used for debugging JIT compiled code, and is
442 // not intended for production use.
443 # if defined(_WIN32)
444 # define RR_LOG(msg, ...) Print(__FUNCSIG__, __FILE__, static_cast<int>(__LINE__), msg "\n", ##__VA_ARGS__)
445 # else
446 # define RR_LOG(msg, ...) Print(__PRETTY_FUNCTION__, __FILE__, static_cast<int>(__LINE__), msg "\n", ##__VA_ARGS__)
447 # endif
448
449 // Macro magic to perform variadic dispatch.
450 // See: https://renenyffenegger.ch/notes/development/languages/C-C-plus-plus/preprocessor/macros/__VA_ARGS__/count-arguments
451 // Note, this doesn't attempt to use the ##__VA_ARGS__ trick to handle 0
452 # define RR_MSVC_EXPAND_BUG(X) X // Helper macro to force expanding __VA_ARGS__ to satisfy MSVC compiler.
453 # define RR_GET_NTH_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N
454 # define RR_COUNT_ARGUMENTS(...) RR_MSVC_EXPAND_BUG(RR_GET_NTH_ARG(__VA_ARGS__, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
455 static_assert(1 == RR_COUNT_ARGUMENTS(a), "RR_COUNT_ARGUMENTS broken"); // Sanity checks.
456 static_assert(2 == RR_COUNT_ARGUMENTS(a, b), "RR_COUNT_ARGUMENTS broken");
457 static_assert(3 == RR_COUNT_ARGUMENTS(a, b, c), "RR_COUNT_ARGUMENTS broken");
458
459 // RR_WATCH_FMT(...) resolves to a string literal that lists all the
460 // arguments by name. This string can be passed to LOG() to print each of
461 // the arguments with their name and value.
462 //
463 // RR_WATCH_FMT(...) uses the RR_COUNT_ARGUMENTS helper macro to delegate to a
464 // corresponding RR_WATCH_FMT_n specialization macro below.
465 # define RR_WATCH_CONCAT(a, b) a##b
466 # define RR_WATCH_CONCAT2(a, b) RR_WATCH_CONCAT(a, b)
467 # define RR_WATCH_FMT(...) RR_MSVC_EXPAND_BUG(RR_WATCH_CONCAT2(RR_WATCH_FMT_, RR_COUNT_ARGUMENTS(__VA_ARGS__))(__VA_ARGS__))
468 # define RR_WATCH_FMT_1(_1) "\n " # _1 ": {0}"
469 # define RR_WATCH_FMT_2(_1, _2) \
470 RR_WATCH_FMT_1(_1) \
471 "\n " #_2 ": {1}"
472 # define RR_WATCH_FMT_3(_1, _2, _3) \
473 RR_WATCH_FMT_2(_1, _2) \
474 "\n " #_3 ": {2}"
475 # define RR_WATCH_FMT_4(_1, _2, _3, _4) \
476 RR_WATCH_FMT_3(_1, _2, _3) \
477 "\n " #_4 ": {3}"
478 # define RR_WATCH_FMT_5(_1, _2, _3, _4, _5) \
479 RR_WATCH_FMT_4(_1, _2, _3, _4) \
480 "\n " #_5 ": {4}"
481 # define RR_WATCH_FMT_6(_1, _2, _3, _4, _5, _6) \
482 RR_WATCH_FMT_5(_1, _2, _3, _4, _5) \
483 "\n " #_6 ": {5}"
484 # define RR_WATCH_FMT_7(_1, _2, _3, _4, _5, _6, _7) \
485 RR_WATCH_FMT_6(_1, _2, _3, _4, _5, _6) \
486 "\n " #_7 ": {6}"
487 # define RR_WATCH_FMT_8(_1, _2, _3, _4, _5, _6, _7, _8) \
488 RR_WATCH_FMT_7(_1, _2, _3, _4, _5, _6, _7) \
489 "\n " #_8 ": {7}"
490 # define RR_WATCH_FMT_9(_1, _2, _3, _4, _5, _6, _7, _8, _9) \
491 RR_WATCH_FMT_8(_1, _2, _3, _4, _5, _6, _7, _8) \
492 "\n " #_9 ": {8}"
493 # define RR_WATCH_FMT_10(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
494 RR_WATCH_FMT_9(_1, _2, _3, _4, _5, _6, _7, _8, _9) \
495 "\n " #_10 ": {9}"
496 # define RR_WATCH_FMT_11(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \
497 RR_WATCH_FMT_10(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \
498 "\n " #_11 ": {10}"
499 # define RR_WATCH_FMT_12(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \
500 RR_WATCH_FMT_11(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \
501 "\n " #_12 ": {11}"
502 # define RR_WATCH_FMT_13(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \
503 RR_WATCH_FMT_12(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \
504 "\n " #_13 ": {12}"
505 # define RR_WATCH_FMT_14(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \
506 RR_WATCH_FMT_13(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \
507 "\n " #_14 ": {13}"
508 # define RR_WATCH_FMT_15(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \
509 RR_WATCH_FMT_14(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \
510 "\n " #_15 ": {14}"
511 # define RR_WATCH_FMT_16(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
512 RR_WATCH_FMT_15(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \
513 "\n " #_16 ": {15}"
514 # define RR_WATCH_FMT_17(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \
515 RR_WATCH_FMT_16(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \
516 "\n " #_17 ": {16}"
517 # define RR_WATCH_FMT_18(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \
518 RR_WATCH_FMT_17(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \
519 "\n " #_18 ": {17}"
520
521 // RR_WATCH() is a helper that prints the name and value of all the supplied
522 // arguments.
523 // For example, if you had the Int and bool variables 'foo' and 'bar' that
524 // you want to print, you can simply write:
525 // RR_WATCH(foo, bar)
526 // When this JIT compiled code is executed, it will print the string
527 // "foo: 1, bar: true" to stdout.
528 //
529 // RR_WATCH() is intended to be used for debugging JIT compiled code, and
530 // is not intended for production use.
531 # define RR_WATCH(...) RR_LOG(RR_WATCH_FMT(__VA_ARGS__), __VA_ARGS__)
532
533 } // namespace rr
534
535 # define RR_PRINT_ONLY(x) x
536 #else
537 # define RR_PRINT_ONLY(x)
538 #endif // ENABLE_RR_PRINT
539
540 #endif // rr_Print_hpp
541