ZeroErr
Loading...
Searching...
No Matches
print.h
Go to the documentation of this file.
1#pragma once
2
3// libc++ offers compatibility-only transitive includes through C++20.
4// Suppress those while loading our explicit dependencies, then restore the
5// caller's setting. This does not change standard-library types or ABI.
6#pragma push_macro("_LIBCPP_REMOVE_TRANSITIVE_INCLUDES")
7#ifndef _LIBCPP_REMOVE_TRANSITIVE_INCLUDES
8#define _LIBCPP_REMOVE_TRANSITIVE_INCLUDES
9#endif
10
12
13#include "zeroerr/color.h"
15#include "zeroerr/buffer.h"
17#include <functional>
18
19#include <memory>
20#include <initializer_list>
21#include <typeinfo>
22
23#if defined(ZEROERR_ENABLE_PFR) && (ZEROERR_CXX_STANDARD >= 14)
24#include "pfr.hpp"
25#endif
26
27#if defined(ZEROERR_ENABLE_MAGIC_ENUM) && (ZEROERR_CXX_STANDARD >= 17)
28#include "magic_enum.hpp"
29#endif
30
31#if defined(ZEROERR_ENABLE_DSVIZ)
32#include "dsviz.h"
33#endif
34
36
37
38namespace zeroerr {
39
40
41constexpr unsigned max_rank = 5;
42
43
44struct Printer;
45template <typename T>
46void PrinterExt(Printer&, const T&, unsigned, const char*, rank<0>);
47
48namespace detail {
49
53template <typename T, typename = void>
54struct has_extension : std::false_type {};
55
56template <typename T>
60
61template <typename T>
62struct has_extension<T, has_printer_ext<T>> : std::true_type {};
63
64#if defined(ZEROERR_ENABLE_PFR) && ZEROERR_CXX_STANDARD >= 14 && !PFR_USE_CPP17 && !PFR_USE_LOOPHOLE
65// Check the bundled classic backend's flat-field constraint before instantiating get().
66// The primary template prevents probing strings and other non-trivial classes.
67template <class T, bool = std::is_class<T>::value && std::is_trivial<T>::value &&
68 std::is_standard_layout<T>::value>
69struct is_classic_pfr_printable : std::false_type {};
70
71template <class T>
72struct is_classic_pfr_printable<T, true>
73 : std::integral_constant<bool, pfr::detail::is_flat_refelectable<T>(
74 pfr::detail::make_index_sequence<pfr::detail::fields_count<T>()>{})> {};
75#endif
76
77} // namespace detail
78
79
87struct Printer {
88 template <typename... Args>
89 Printer& operator()(Args&&... args) {
91 call(std::forward<Args>(args)...);
92 return *this;
93 }
94 template <typename T>
95 Printer& operator()(std::initializer_list<T>&& value) {
97 call(std::forward<decltype(value)>(value));
98 return *this;
99 }
100
104
105 template <typename T, typename... V>
106 void call(T&& value, V&&... others) {
107 PrinterExt(*this, std::forward<T>(value), 0, " ", rank<max_rank>{});
108 call(std::forward<V>(others)...);
109 }
110
111 template <typename T>
112 void call(T&& value) {
113 PrinterExt(*this, std::forward<T>(value), 0, "", rank<max_rank>{});
114 os << line_break;
115 os.flush();
116 }
117
118private:
119 StringBuffer storage_;
120public:
121 Printer() : os(storage_) { use_stringstream = true; }
122 template <class Stream>
123 explicit Printer(Stream& stream) : os(storage_) { storage_.attach(stream); }
124 Printer(const Printer& other) : storage_(other.storage_), isColorful(other.isColorful),
125 isCompact(other.isCompact), isQuoted(other.isQuoted), indent(other.indent),
126 line_break(other.line_break), os(storage_), use_stringstream(other.use_stringstream),
128
129 bool isColorful = true; // colorful output
130 bool isCompact = false; // compact mode
131 bool isQuoted = true; // string is quoted
132 int indent = 2;
133 const char* line_break = "\n";
135 bool use_stringstream = false;
137
138 template <class T>
139 static zeroerr::String type(const T& t) {
140 return demangle(typeid(t).name());
141 }
142
143#if defined(ZEROERR_ENABLE_MAGIC_ENUM) && (ZEROERR_CXX_STANDARD >= 17)
145 print(const T& value, unsigned level, const char* lb, rank<0>) { os << tab(level) << magic_enum::enum_name(value) << lb; }
146#else
147 ZEROERR_ENABLE_IF((ZEROERR_IS_ENUM && detail::is_streamable<std::ostream, T>::value))
148 print(const T& value, unsigned level, const char* lb, rank<0>) {
149 os << tab(level) << value << lb;
150 }
151
153 print(const T& value, unsigned level, const char* lb, rank<0>) {
154 os << tab(level) << "<unprintable enum " << type(value) << ">" << lb;
155 }
156#endif
157
159 print(const T& value, unsigned level, const char* lb, rank<0>) { os << tab(level) << value << lb; }
160
162 !std::is_function<typename std::remove_pointer<T>::type>::value)
163 print(const T& value, unsigned level, const char* lb, rank<0>) {
164 if (value == nullptr)
165 os << tab(level) << "nullptr" << lb;
166 else
167 os << tab(level) << "<" << type(value) << " at " << static_cast<const void*>(value) << ">" << lb;
168 }
169
171 std::is_function<typename std::remove_pointer<T>::type>::value)
172 print(T value, unsigned level, const char* lb, rank<1>) {
173 if (value == nullptr)
174 os << tab(level) << "nullptr" << lb;
175 else
176 os << tab(level) << "<" << type(value) << " at "
177 << reinterpret_cast<const void*>(value) << ">" << lb;
178 }
179
180 template <class R, class... Args>
181 void print(const std::function<R(Args...)>& value, unsigned level, const char* lb, rank<3>) {
182 if (!value)
183 os << tab(level) << "nullptr" << lb;
184 else
185 os << tab(level) << "<" << type(value) << " holding "
186 << demangle(value.target_type().name()) << ">" << lb;
187 }
188
189
190 // Preserve the pointer fallback for built-in arrays; character arrays use
191 // the higher-priority string rule.
192 template <typename T, std::size_t N>
193 void print(const T (&value)[N], unsigned level, const char* lb, rank<0>) {
194 print(value + 0, level, lb, rank<max_rank>{});
195 }
196
198 print(const T& value, unsigned level, const char* lb, rank<0>) {
199 os << tab(level) << type(value) << lb;
200 }
201
202
204 print(const T& value, unsigned level, const char* lb, rank<1>) {
205 os << tab(level) << '\'' << value << '\'' << lb;
206 }
207
208#if defined(ZEROERR_ENABLE_PFR) && (ZEROERR_CXX_STANDARD >= 14)
209 template <class StructType, unsigned... I>
210 void print_struct(const StructType& s, unsigned, const char*, detail::seq<I...>) {
211 int _[] = {(os << (I == 0 ? "" : ", ") << pfr::get<I>(s), 0)...};
212 (void)_;
213 }
214
215#if PFR_USE_CPP17 || PFR_USE_LOOPHOLE
217#else
218 ZEROERR_ENABLE_IF(detail::is_classic_pfr_printable<T>::value)
219#endif
220 print(const T& value, unsigned level, const char* lb, rank<1>) {
221 os << tab(level) << "{";
222 print_struct(value, level, isCompact ? " " : line_break,
223 detail::gen_seq<pfr::tuple_size<T>::value>{});
224 os << tab(level) << "}" << lb;
225 }
226#endif
227
229 print(const T& value, unsigned level, const char* lb, rank<2>) {
230 os << tab(level) << (value ? "true" : "false") << lb;
231 }
232
234 print(const T& value, unsigned level, const char* lb, rank<2>) { os << tab(level) << value << lb; }
235
236
238 print(const T& value, unsigned level, const char* lb, rank<2>) {
239 os << tab(level) << "{" << (isCompact ? "" : line_break);
240 bool last = false;
241 for (auto iter = value.begin(); iter != value.end(); ++iter) {
242 auto next = iter;
243 ++next;
244 if (next == value.end()) last = true;
245 print(*iter, level + 1, isCompact ? (last ? "" : ", ") : line_break, rank<max_rank>{});
246 }
247 os << tab(level) << "}" << lb;
248 }
249
251 print(const T& value, unsigned level, const char* lb, rank<3>) {
252 os << tab(level) << "[";
253 bool last = false;
254 for (auto iter = value.begin(); iter != value.end(); ++iter) {
255 auto next = iter;
256 ++next;
257 if (next == value.end()) last = true;
258 print(*iter, 0, last ? "" : ", ", rank<max_rank>{});
259 }
260 os << tab(level) << "]" << lb;
261 }
262
263
265 print(const T& value, unsigned level, const char* lb, rank<3>) {
266 if (value.get() == nullptr)
267 os << tab(level) << "nullptr" << lb;
268 else
269 os << tab(level) << "<" << type(value) << " at " << static_cast<const void*>(value.get()) << ">" << lb;
270 }
271
273 !ZEROERR_IS_COMPLEX && !detail::is_weak_pointer_like<T>::value)
274 print(const T& value, unsigned level, const char* lb, rank<4>) {
275 os << tab(level) << "{" << (isCompact ? "" : line_break);
276 bool last = false;
277 for (auto iter = value.begin(); iter != value.end(); ++iter) {
278 auto next = iter;
279 ++next;
280 if (next == value.end()) last = true;
281 print((*iter).first, level + 1, " : ", rank<max_rank>{});
282 print((*iter).second, level + 1, isCompact ? (last ? "" : ", ") : line_break,
283 rank<max_rank>{});
284 }
285 os << tab(level) << "}" << lb;
286 }
287
288 ZEROERR_ENABLE_IF(detail::is_weak_pointer_like<T>::value && !ZEROERR_IS_STRING && !ZEROERR_IS_COMPLEX)
289 print(const T& value, unsigned level, const char* lb, rank<4>) {
290 const auto locked = value.lock();
291 print(locked, level, lb, rank<3>{});
292 }
293
295 print(const T& value, unsigned level, const char* lb, rank<4>) {
296 os << tab(level) << "(" << value.real() << "+" << value.imag() << "i)" << lb;
297 }
298
300 print(const T& value, unsigned level, const char* lb, rank<4>) {
301 os << tab(level) << quote();
302 write_string(value, typename detail::is_string_like<T>::type{});
303 os << quote() << lb;
304 }
305
306 template <class T>
307 void write_string(const T& value, std::true_type) {
308 if (value.size() != 0)
309 os.write(value.data(), static_cast<std::size_t>(value.size()));
310 }
311 template <class T>
312 void write_string(const T& value, std::false_type) {
313 const char* text = value;
314 os << (text ? text : "nullptr");
315 }
316
317 template <class TupType>
318 inline void print_tuple(const TupType&, unsigned, const char*, detail::seq<>) {}
319
320 template <class TupType, unsigned... I>
321 inline void print_tuple(const TupType& _tup, unsigned level, const char*, detail::seq<I...>) {
322 int _[] = {(os << (I == 0 ? "" : ", "),
323 print(detail::tuple_get<I>(_tup), level + 1, "", rank<max_rank>{}), 0)...};
324 (void)_;
325 }
326
327 ZEROERR_ENABLE_IF(detail::is_tuple_like<T>::value && !ZEROERR_IS_CONTAINER && !ZEROERR_IS_AUTOPTR)
328 print(const T& value, unsigned level, const char* lb, rank<3>) {
329 os << tab(level) << "(";
330 print_tuple(value, level, isCompact ? " " : line_break, detail::gen_seq<std::tuple_size<T>::value>{});
331 os << ")" << lb;
332 }
333
334 void print_text(const char* value) { os << value; }
335 template <class T> void print_text(const T& value) { os.write(value.data(), value.size()); }
336 zeroerr::String tab(unsigned level);
337 const char* quote() { return isQuoted ? "\"" : ""; }
338
339 static zeroerr::String demangle(const char* name);
340
341 zeroerr::String str() const;
342 operator zeroerr::String() const { return str(); }
343 template <class T, typename std::enable_if<detail::StringLike<T>::owning &&
344 !std::is_same<T, String>::value, int>::type = 0>
345 operator T() const { return str(); }
346
347};
348
349inline StringBuffer& operator<<(StringBuffer& buffer, const Printer& printer) {
350 if (printer.use_stringstream) buffer << printer.str();
351 return buffer;
352}
353
354namespace detail {
355
356// Only the concrete value dispatch is templated. Stream construction, printing
357// order, formatting and cleanup all live in the shared implementation.
358template <typename T>
359void printDiagnosticValue(Printer& printer, const void* value) {
360 printer.call(*static_cast<T*>(const_cast<void*>(value)));
361}
362
363template <typename T>
365 return {const_cast<const void*>(static_cast<const volatile void*>(std::addressof(value))),
367}
368
369} // namespace detail
370
387template <class T>
388void PrinterExt(Printer& P, const T& v, unsigned level, const char* lb, rank<0>) {
389 P.print(v, level, lb, rank<max_rank>{});
390}
391
392extern Printer& getStdoutPrinter();
393extern Printer& getStderrPrinter();
394
395
396} // namespace zeroerr
397
399
400#pragma pop_macro("_LIBCPP_REMOVE_TRANSITIVE_INCLUDES")
Definition buffer.h:7
Definition string.h:41
#define ZEROERR_SUPPRESS_COMMON_WARNINGS_POP
Definition config.h:283
#define ZEROERR_SUPPRESS_COMMON_WARNINGS_PUSH
Definition config.h:235
Definition assert_text.cpp:6
auto tuple_get(T &&value) -> decltype(get< I >(std::forward< T >(value)))
Definition typetraits.h:182
void void_t
Definition typetraits.h:55
void printDiagnosticValue(Printer &printer, const void *value)
Definition print.h:359
void_t< decltype(zeroerr::PrinterExt(std::declval< zeroerr::Printer & >(), std::declval< T & >(), 0, nullptr, zeroerr::rank< zeroerr::max_rank >()))> has_printer_ext
Definition print.h:57
DiagnosticValue makeDiagnosticValue(T &value)
Definition print.h:364
Definition assert_text.cpp:5
StringBuffer & operator<<(StringBuffer &buffer, const Printer &printer)
Definition print.h:349
Printer & getStderrPrinter()
Definition print.cpp:14
void PrinterExt(Printer &, const T &, unsigned, const char *, rank< 0 >)
PrinterExt is an extension of Printer that allows user to write custom rules for printing.
Definition print.h:388
Printer & getStdoutPrinter()
Definition print.cpp:9
constexpr unsigned max_rank
Definition print.h:41
A functor class Printer for printing a value of any type.
Definition print.h:87
Printer()
Definition print.h:121
int indent
Definition print.h:132
print(const T &value, unsigned level, const char *lb, rank< 0 >)
Definition print.h:148
bool clear_stream_before_printing
Definition print.h:136
unsigned const char * lb
Definition print.h:163
Printer & operator()(Args &&... args)
Definition print.h:89
Printer(const Printer &other)
Definition print.h:124
StringBuffer & os
Definition print.h:134
bool isColorful
Definition print.h:129
unsigned const char rank< 0 >
Definition print.h:163
bool isQuoted
Definition print.h:131
void call(T &&value, V &&... others)
Definition print.h:106
static zeroerr::String type(const T &t)
Definition print.h:139
bool isCompact
Definition print.h:130
Printer(Stream &stream)
Definition print.h:123
Printer & operator()(std::initializer_list< T > &&value)
Definition print.h:95
void call(T &&value)
Definition print.h:112
void check_stream()
Definition print.h:101
ZEROERR_ENABLE_IF(ZEROERR_IS_POINTER &&!std::is_function< typename std::remove_pointer< T >::type >::value) print(const T &value
bool use_stringstream
Definition print.h:135
const char * line_break
Definition print.h:133
unsigned level
Definition print.h:163
has_extension is a type trait to check if user defined PrinterExt for a type
Definition print.h:54
Definition typetraits.h:97
rank is a helper class for Printer to define the priority of overloaded functions.
Definition typetraits.h:43
#define last
Definition table.cpp:255
#define ZEROERR_IS_MAP
Definition typetraits.h:351
#define ZEROERR_IS_ENUM
Definition typetraits.h:339
#define ZEROERR_IS_CONTAINER
Definition typetraits.h:340
#define ZEROERR_IS_POD
Definition typetraits.h:352
#define ZEROERR_IS_FLOAT
Definition typetraits.h:338
#define ZEROERR_IS_INT
Definition typetraits.h:337
#define ZEROERR_IS_POINTER
Definition typetraits.h:342
#define ZEROERR_IS_CLASS
Definition typetraits.h:345
#define ZEROERR_IS_STREAMABLE
Definition typetraits.h:346
#define ZEROERR_IS_CHAR
Definition typetraits.h:343
#define ZEROERR_IS_WCHAR
Definition typetraits.h:344
#define ZEROERR_IS_STRING
Definition typetraits.h:341
#define ZEROERR_IS_AUTOPTR
Definition typetraits.h:350
#define ZEROERR_IS_COMPLEX
Definition typetraits.h:348
#define ZEROERR_IS_BOOL
Definition typetraits.h:349
#define ZEROERR_ENABLE_IF(x)
Definition typetraits.h:334
#define ZEROERR_IS_ARRAY
Definition typetraits.h:347