1
#ifndef CHILON_PRINT_HPP
2
#define CHILON_PRINT_HPP
3
4
#include <chilon/variant.hpp>
5
#include <chilon/iterator_range.hpp>
6
#include <chilon/type_name.hpp>
7
8
#include <chilon/meta/boolean.hpp>
9
#include <chilon/meta/make.hpp>
10
#include <chilon/meta/require.hpp>
11
#include <chilon/meta/nothing.hpp>
12
13
#include <iostream>
14
#include <vector>
15
#include <set>
16
#include <unordered_map>
17
#include <map>
18
19
#ifndef CHILON_INDENT
20
/// This macro is used to determine what to use as an indent. It defaults to 4
21
/// spaces if you do not define it.
22
#define CHILON_INDENT "    "
23
#endif
24
25
// define this to print "variant: " before variant types
26
// #define CHILON_PRINT_PREFIX_VARIANTS
27
28
namespace chilon {
29
30
namespace detail { struct printable {}; }
31
32
namespace color {
33
    struct type {
34
        char const *value_;
35
        type(char const *value) : value_(value) {};
36
    };
37
38
    auto const black = type("\033[1;30m");
39
    auto const red = type("\033[1;31m");
40
    auto const green = type("\033[1;32m");
41
    auto const brown = type("\033[1;33m");
42
    auto const blue = type("\033[1;34m");
43
    auto const magenta = type("\033[1;35m");
44
    auto const cyan = type("\033[1;36m");
45
    auto const white = type("\033[1;37m");
46
47
    auto const neutral = type("\033[0;m");
48
}
49
50
/**
51
 * @brief Inherit from this to create a custom printable type.
52
 * @tparam Class which much contain the function
53
 *  template <class S, class V>
54
 *  exec(int const indent, S& stream, V const& value)
55
 */
56
template <class F>
57
struct printable : detail::printable {
58
    typedef F printer_type;
59
};
60
61
62
/**
63
 * Print the indent to a stream the indicated number of times.
64
 * @tparam O Type of stream.
65
 * @param indent Number of times to repeat the indent.
66
 * @param stream Stream to output to.
67
 */
68
template <class O>
69
inline void print_indent(int const indent, O& stream) {
70
    for (int i = 0; i < indent; ++i) stream << CHILON_INDENT;
71
}
72
73
/**
74
 * Default print_args sends the type to the stream's << operator. This
75
 * function is called through ADL so you can override it.
76
 * @tparam O Type of stream.
77
 * @tparam H Type of value to print.
78
 * @param indent Number of times to repeat the indent.
79
 * @param stream Stream to output to.
80
 * @param head Value to output.
81
 */
82
83
template <class O, class T, class = decltype(meta::make<O>() << "t")>
84
inline void print(O& stream, T const& item) {
85
    stream << item;
86
}
87
88
template <class O, class = decltype(meta::make<O>() << "t")>
89
inline void print(O& stream, color::type const& color) {
90
    stream << color.value_;
91
}
92
93
/**
94
 * print override for character prints the character enclosed in single
95
 * quotes. If the character is a quote, then a backslash is printed before it.
96
 * @tparam O Type of stream.
97
 * @param indent Number of times to repeat the indent.
98
 * @param stream Stream to output to.
99
 * @param ch Character to output.
100
 */
101
template <class O>
102
inline void print(O& stream, char const ch) {
103
    if (ch == '\'')
104
        stream << "\'\\" << ch << '\'';
105
    else
106
        stream << '\'' << ch << '\'';
107
}
108
109
/**
110
 * print override for range prints the range in double quotes. Double
111
 * quotes are escaped with a backslash.
112
 * @tparam O Type of stream.
113
 * @param indent Number of times to repeat the indent.
114
 * @param stream Stream to output to.
115
 * @param head range to output.
116
 */
117
template <class O>
118
inline void print(O& stream, range const& head) {
119
    auto it = std::find(head.begin(), head.end(), '"');
120
    if (it == head.end()) {
121
        stream << '"' << head << '"';
122
    }
123
    else {
124
        auto delimited_begin = head.begin();
125
        stream << '"';
126
        do {
127
            stream << range(delimited_begin, it) << "\\\"";
128
            delimited_begin = it + 1;
129
            it = std::find(delimited_begin, head.end(), '"');
130
        } while (it != head.end());
131
        stream << '"';
132
    }
133
}
134
135
template <class O, class H>
136
inline CHILON_RETURN_NOT_REQUIRE(void, std::is_base_of<detail::printable, H>)
137
print_args(int const indent, O& stream, H const& head) {
138
    print(stream, head);
139
}
140
141
template <class O>
142
void print_args(int const indent, O& stream) {}
143
144
template <class O, class H>
145
inline CHILON_RETURN_REQUIRE(void, std::is_base_of<detail::printable, H>)
146
print_args(int const indent, O& stream, H const& head) {
147
    H::printer_type::exec(indent, stream, head);
148
}
149
150
/**
151
 * print_tuple override for last item in a tuple, calls print_args on last item
152
 * element through ADL.
153
 * @tparam Idx Index of tuple element to print.
154
 * @tparam O Type of stream.
155
 * @tparam T... Pack of types in tuple.
156
 * @param indent Number of times to repeat the indent.
157
 * @param stream Stream to output to.
158
 * @param t Tuple to output.
159
 */
160
template <int Idx, class O, class... T>
161
typename std::enable_if<(Idx == (sizeof...(T) - 1))>::type
162
print_tuple(int const indent, O& stream, std::tuple<T...> const& t) {
163
    print_indent(indent + 1, stream);
164
    print_args(indent + 1, stream, std::get<Idx>(t));
165
}
166
167
/**
168
 * print_tuple used for printing tuples.
169
 * @tparam Idx Index of tuple element to print.
170
 * @tparam O Type of stream.
171
 * @tparam T... Pack of types in tuple.
172
 * @param indent Number of times to repeat the indent.
173
 * @param stream Stream to output to.
174
 * @param t Tuple to output.
175
 */
176
template <int Idx, class O, class... T>
177
typename std::enable_if<(Idx < (sizeof...(T) - 1))>::type
178
print_tuple(int const indent, O& stream, std::tuple<T...> const& t) {
179
    print_indent(indent + 1, stream);
180
    print_args(indent + 1, stream, std::get<Idx>(t));
181
    stream << '\n';
182
    print_tuple<Idx + 1>(indent, stream, t);
183
}
184
185
/**
186
 * print_args override for tuples, uses print_tuple.
187
 * @tparam O Type of stream.
188
 * @tparam T... Pack of types in tuple.
189
 * @param indent Number of times to repeat the indent.
190
 * @param stream Stream to output to.
191
 * @param t Tuple to output.
192
 */
193
template <class O, class... T>
194
inline void print_args(int const indent, O& stream, std::tuple<T...> const& t) {
195
    stream << "(\n";
196
    print_tuple<0>(indent, stream, t);
197
    stream << "\n";
198
    print_indent(indent, stream);
199
    stream << ")";
200
}
201
202
template <class O, class... T>
203
inline void print_args(int const indent, O& stream, std::tuple<> const& t) {
204
    stream << "()";
205
}
206
207
/**
208
 * print_args override for vector, prints the "[" header. A new line
209
 * and indenting is printed before each item in the vector. print_args is
210
 * called to print each vector element through ADL.
211
 * @tparam O Type of stream.
212
 * @tparam T Type of vector element.
213
 * @param indent Number of times to repeat the indent.
214
 * @param stream Stream to output to.
215
 * @param t Vector to output.
216
 */
217
template <class O, class T>
218
inline void print_args(int const indent, O& stream, std::vector<T> const& head) {
219
    if (head.empty()) {
220
        stream << "[]";
221
    }
222
    else if (1 == head.size()) {
223
        stream << "[ ";
224
        print_args(indent, stream, head.front());
225
        stream << " ]";
226
    }
227
    else {
228
        stream << "[\n";
229
        auto it = head.begin();
230
        print_indent(indent + 1, stream);
231
        print_args(indent + 1, stream, *it);
232
233
        while (++it != head.end()) {
234
            stream << "\n";
235
            print_indent(indent + 1, stream);
236
            print_args(indent + 1, stream, *it);
237
        }
238
239
        stream << '\n';
240
        print_indent(indent, stream);
241
        stream << ']';
242
    }
243
}
244
245
template <class O, class T>
246
inline void print_map(int const indent, O& stream, T const& head);
247
248
template <class O, class H1, class H2, class H3, class H4, class... T>
249
inline void print_args(int const                       indent,
250
                       O&                              stream,
251
                       std::map<H1, H2, H3, H4> const& head,
252
                       T const&...                     tail)
253
{
254
    print_map(indent, stream, head);
255
    print_args(indent, stream, tail...);
256
}
257
258
template <class O, class H1, class H2, class H3, class H4, class H5, class... T>
259
inline void print_args(int const                                     indent,
260
                       O&                                            stream,
261
                       std::unordered_map<H1, H2, H3, H4, H5> const& head,
262
                       T const&...                                   tail)
263
{
264
    print_map(indent, stream, head);
265
    print_args(indent, stream, tail...);
266
}
267
268
/**
269
 * print_args override for multiple arguments. Each argument is printed in
270
 * turn by calling print_args on it through ADL.
271
 * @tparam O Type of stream.
272
 * @tparam H Type of first argument.
273
 * @tparam T... Parameter pack for subsequent arguments.
274
 * @param indent Number of times to repeat the indent.
275
 * @param stream Stream to output to.
276
 * @param head First argument to print.
277
 * @param tail Pack of subsequent arguments.
278
 */
279
template <class O, class H, class... T>
280
inline void print_args(int const indent, O& stream, H const& head, T const&... tail) {
281
    print_args(indent, stream, head);
282
    print_args(indent, stream, tail...);
283
}
284
285
template <class O, class T>
286
inline void print_map(int const indent, O& stream, T const& head) {
287
    if (head.empty()) {
288
        stream << "[]";
289
    }
290
    else {
291
        stream << "[\n";
292
        auto it = head.begin();
293
        print_indent(indent + 1, stream);
294
        print_args(indent + 1, stream, it->first, ": ", it->second);
295
296
        while (++it != head.end()) {
297
            stream << "\n";
298
            print_indent(indent + 1, stream);
299
            print_args(indent + 1, stream, it->first, ": ", it->second);
300
        }
301
302
        stream << '\n';
303
        print_indent(indent, stream);
304
        stream << ']';
305
    }
306
}
307
308
/// Printer functor.
309
template <class Stream = std::ostream>
310
struct printer {
311
    template <class T>
312
    void operator()(T const& value) {
313
        println(stream_, value);
314
    }
315
316
    printer(Stream& stream) : stream_(stream) {}
317
    Stream& stream_;
318
};
319
320
template <class Stream = std::ostream>
321
struct tail_printer {
322
    template <class T>
323
    void operator()(T const& t) const {
324
        print_args(indent_, stream_, t);
325
    }
326
327
    tail_printer(int const indent, Stream& stream)
328
      : indent_(indent), stream_(stream) {}
329
  protected:
330
    int const indent_;
331
    Stream&   stream_;
332
};
333
334
template <class Stream = std::ostream>
335
struct variant_tail_printer : tail_printer<Stream> {
336
    void operator()() const {
337
        print_args(this->indent_, this->stream_, "empty");
338
    }
339
340
    template <class T>
341
    void operator()(T const& t) const {
342
        tail_printer<Stream>::operator()(t);
343
    }
344
345
    variant_tail_printer(int const indent, Stream& stream)
346
      : tail_printer<Stream>(indent, stream) {}
347
};
348
349
/**
350
 * print type stored in the variant by calling print_args through ADL on the variant.
351
 * @tparam O Type of stream.
352
 * @tparam T Type of vector element.
353
 * @param indent Number of times to repeat the indent.
354
 * @param stream Stream to output to.
355
 * @param t variant to output.
356
 */
357
template <class O, class... T>
358
inline void print_variant(int const indent, O& stream, variant<T...> const& t) {
359
    variant_apply(t, variant_tail_printer<O>(indent, stream));
360
}
361
362
/**
363
 * print_args override for variant, prints a "variant: " header followed by the
364
 * type stored in the variant by calling print_args through ADL on this type.
365
 * @tparam O Type of stream.
366
 * @tparam T Type of vector element.
367
 * @param indent Number of times to repeat the indent.
368
 * @param stream Stream to output to.
369
 * @param t variant to output.
370
 */
371
template <class O, class... T>
372
inline void print_args(int const indent, O& stream, variant<T...> const& t) {
373
#ifdef CHILON_PRINT_PREFIX_VARIANTS
374
    stream << "variant: ";
375
#endif
376
    print_variant(indent, stream, t);
377
};
378
379
/**
380
 * print_args with no indent supplied calls print_args with an indent of 0.
381
 * @tparam O Type of stream.
382
 * @tparam T Parameter pack of argument types.
383
 * @param stream Stream to output to.
384
 * @param t  Pack of arguments to print.
385
 */
386
template <class O, class... T>
387
inline void print_args(O& stream, T const &... t) {
388
    print_args(0, stream, t...);
389
}
390
391
template <class O, class... T, class = decltype(meta::make<O>() << "t")>
392
inline void print(O& stream, T const&... tail) {
393
    print_args(0, stream, tail...);
394
}
395
396
/**
397
 * When the first type of the argument is not a stream (tested by looking for
398
 * a `operator<<(char const[])' method, then use std::cout as the stream.
399
 * @tparam T Parameter pack of argument types.
400
 * @param t  Pack of arguments to print.
401
 */
402
template <class... T>
403
inline void print(T const&... tail) {
404
    print(std::cout, tail...);
405
}
406
407
template <class O, class... T, class = decltype(meta::make<O>() << "t")>
408
inline void println(O& stream, T const&... tail) {
409
    print(stream, tail..., "\n");
410
}
411
412
template <class O, class... T, class = decltype(meta::make<O>() << "t")>
413
inline void println(O& stream, color::type const& col, T const&... tail) {
414
    print(stream, col, tail..., color::neutral, "\n");
415
}
416
417
template <class... T>
418
inline void println(T const&... tail) {
419
    println(std::cout, tail...);
420
}
421
422
#ifdef NDEBUG
423
template <class... T>
424
inline void println_debug(T const&...) {}
425
#else
426
template <class O, class... T, class = decltype(meta::make<O>() << "t")>
427
inline void println_debug(O& stream, T const&... tail) {
428
    println(stream, tail...);
429
}
430
431
template <class... T>
432
inline void println_debug(T const&... tail) {
433
    println(std::cerr, tail...);
434
}
435
#endif
436
437
/// Printer functor factory.
438
template <class Stream>
439
inline printer<Stream> make_printer(Stream& stream) {
440
    return printer<Stream>(stream);
441
}
442
443
template <class O, class J>
444
void print_join(O& stream, J const& join) {}
445
446
template <class O, class J, class H>
447
void print_join(O& stream, J const& join, H const& head) {
448
    chilon::print(stream, head);
449
}
450
451
template <class O, class J, class H>
452
void print_join(O& stream, J const& join, std::vector<H> const& head) {
453
    auto it = head.begin();
454
    stream << *it;
455
    for (++it; it != head.end(); ++it) stream << join << *it;
456
}
457
458
template <class O, class J, class H, class U, class V>
459
void print_join(O& stream, J const& join, std::set<H, U, V> const& head) {
460
    auto it = head.begin();
461
    stream << *it;
462
    for (++it; it != head.end(); ++it) stream << join << *it;
463
}
464
465
/// prints items H and Args... joined by J
466
template <class O, class J, class H, class... Args>
467
void print_join(O& stream, J const& join, H const& head, Args const&... args) {
468
    chilon::print(stream, head, join);
469
    print_join(stream, join, args...);
470
}
471
472
}
473
#endif