mimic++ v4
Loading...
Searching...
No Matches
Printer.hpp
Go to the documentation of this file.
1// // Copyright Dominic (DNKpp) Koepke 2024 - 2024.
2// // Distributed under the Boost Software License, Version 1.0.
3// // (See accompanying file LICENSE_1_0.txt or copy at
4// // https://www.boost.org/LICENSE_1_0.txt)
5
6#ifndef MIMICPP_PRINTER_HPP
7#define MIMICPP_PRINTER_HPP
8
9#pragma once
10
11#include "mimic++/Fwd.hpp"
12#include "mimic++/String.hpp"
14#include "mimic++/Utility.hpp"
15
16#include <cstdint>
17#include <functional>
18#include <iterator>
19#include <optional>
20#include <ranges>
21#include <source_location>
22#include <sstream>
23#include <string>
24#include <string_view>
25#include <utility>
26
27#ifndef MIMICPP_CONFIG_USE_FMT
28 #include <format>
29#else
30
31#if __has_include(<fmt/format.h>)
32#include <fmt/format.h>
33#else
34 #error "The fmt formatting backend is explicitly enabled, but the include <fmt/format.h> can not be found."
35#endif
36
37#endif
38
39namespace mimicpp
40{
41 using StringStreamT = std::basic_ostringstream<CharT, CharTraitsT>;
42
43 template <typename T>
44 concept print_iterator = std::output_iterator<T, const CharT&>;
45
46 template <typename Printer, typename OutIter, typename T>
48 && requires(OutIter out)
49 {
50 {
51 Printer::print(out, std::declval<T&&>())
52 } -> std::convertible_to<OutIter>;
53 };
54}
55
57{
58#ifndef MIMICPP_CONFIG_USE_FMT
59
60 // use std format
61#ifndef _LIBCPP_VERSION
62
63 using std::formatter;
64 using std::format;
65 using std::format_to;
66 using std::vformat;
67 using std::vformat_to;
68 using std::make_format_args;
69
70#else
71
72 // libc++ has some serious trouble when using its std::format implementation.
73 // Let's simply redirect any calls to std::vformat instead.
74
75 using std::formatter;
76 using std::vformat;
77 using std::vformat_to;
78 using std::make_format_args;
79
80 template <typename... Args>
81 [[nodiscard]]
82 StringT format(const StringViewT fmt, Args&&... args) // NOLINT(cppcoreguidelines-missing-std-forward)
83 {
84 return format::vformat(
85 fmt,
86 std::make_format_args(args...));
87 }
88
89 template <class OutputIt, typename... Args>
90 OutputIt format_to(const OutputIt out, const StringViewT fmt, Args&&... args) // NOLINT(cppcoreguidelines-missing-std-forward)
91 {
92 return format::vformat_to(
93 std::move(out),
94 fmt,
95 std::make_format_args(args...));
96 }
97
98#endif
99
100 namespace detail
101 {
102 template <typename Char>
103 struct format_context;
104
105 template <typename Char>
106 using format_context_t = typename format_context<Char>::type;
107
108 template <>
109 struct format_context<char>
110 {
111 using type = std::format_context;
112 };
113
114 template <>
115 struct format_context<wchar_t>
116 {
117 using type = std::wformat_context;
118 };
119
130 template <class T, class Char>
131 concept formattable =
132 std::semiregular<std::formatter<std::remove_cvref_t<T>, Char>>
133 && requires(
134 std::formatter<std::remove_cvref_t<T>, Char> formatter,
135 T t,
136 format_context_t<Char> formatContext,
137 std::basic_format_parse_context<Char> parseContext
138 )
139 {
140 { formatter.parse(parseContext) } -> std::same_as<typename std::basic_format_parse_context<Char>::iterator>;
141 {
142 std::as_const(formatter).format(t, formatContext)
143 } -> std::same_as<typename std::remove_reference_t<decltype(formatContext)>::iterator>;
144 };
145 }
146
147 // use fmt format
148#else
149
150 using fmt::formatter;
151 using fmt::format;
152 using fmt::format_to;
153 using fmt::vformat;
154 using fmt::vformat_to;
155 using fmt::make_format_args;
156
157 namespace detail
158 {
159 template <class T, class Char>
160 concept formattable = fmt::is_formattable<std::remove_reference_t<T>, Char>::value;
161 }
162
163#endif
164}
165
166template <>
167struct mimicpp::format::formatter<mimicpp::ValueCategory, mimicpp::CharT>
168 : public formatter<std::string_view, mimicpp::CharT>
169{
171
172 auto format(
173 const ValueCategoryT category,
174 auto& ctx
175 ) const
176 {
177 constexpr auto toString = [](const ValueCategoryT cat)
178 {
179 switch (cat)
180 {
181 case ValueCategoryT::lvalue: return "lvalue";
182 case ValueCategoryT::rvalue: return "rvalue";
183 case ValueCategoryT::any: return "any";
184 }
185
186 throw std::invalid_argument{"Unknown category value."};
187 };
188
189 return formatter<std::string_view, mimicpp::CharT>::format(
190 toString(category),
191 ctx);
192 }
193};
194
195template <>
196struct mimicpp::format::formatter<mimicpp::Constness, mimicpp::CharT>
197 : public formatter<std::string_view, mimicpp::CharT>
198{
200
201 auto format(
202 const ConstnessT category,
203 auto& ctx
204 ) const
205 {
206 constexpr auto toString = [](const ConstnessT value)
207 {
208 switch (value)
209 {
210 case ConstnessT::non_const: return "mutable";
211 case ConstnessT::as_const: return "const";
212 case ConstnessT::any: return "any";
213 }
214
215 throw std::invalid_argument{"Unknown constness value."};
216 };
217
218 return formatter<std::string_view, mimicpp::CharT>::format(
219 toString(category),
220 ctx);
221 }
222};
223
224namespace mimicpp::custom
225{
230 template <typename>
231 class Printer;
232}
233
234namespace mimicpp::detail
235{
236 template <
237 print_iterator OutIter,
238 typename T,
240 OutIter print(
241 OutIter out,
242 T&& value,
243 [[maybe_unused]] const priority_tag<4>
244 )
245 {
246 return Printer::print(
247 std::move(out),
248 std::forward<T>(value));
249 }
250
251 template <typename>
252 class Printer;
253
254 template <
255 print_iterator OutIter,
256 typename T,
257 printer_for<OutIter, T> Printer = Printer<std::remove_cvref_t<T>>>
258 OutIter print(
259 OutIter out,
260 T&& value,
261 [[maybe_unused]] const priority_tag<3>
262 )
263 {
264 return Printer::print(
265 std::move(out),
266 std::forward<T>(value));
267 }
268
269 template <print_iterator OutIter, std::ranges::forward_range Range>
270 OutIter print(
271 OutIter out,
272 Range&& range,
273 priority_tag<2>
274 );
275
276 template <print_iterator OutIter, format::detail::formattable<CharT> T>
277 OutIter print(
278 OutIter out,
279 T&& value,
280 [[maybe_unused]] const priority_tag<1>
281 )
282 {
283 return format::format_to(
284 std::move(out),
285 "{}",
286 std::forward<T>(value));
287 }
288
289 template <print_iterator OutIter>
290 OutIter print(
291 OutIter out,
292 auto&&,
293 [[maybe_unused]] const priority_tag<0>
294 )
295 {
296 return format::format_to(
297 std::move(out),
298 "{{?}}");
299 }
300
301 class PrintFn
302 {
303 public:
304 template <print_iterator OutIter, typename T>
305 OutIter operator ()(
306 OutIter out,
307 T&& value
308 ) const
309 {
310 static_assert(
311 requires(const priority_tag<4> tag)
312 {
313 { print(out, std::forward<T>(value), tag) } -> std::convertible_to<OutIter>;
314 },
315 "The given type is not printable. ");
316
317 return print(
318 std::move(out),
319 std::forward<T>(value),
320 priority_tag<4>{});
321 }
322
323 template <typename T>
324 StringT operator ()(T&& value) const
325 {
326 StringStreamT stream{};
327 std::invoke(
328 *this,
329 std::ostreambuf_iterator{stream},
330 std::forward<T>(value));
331 return std::move(stream).str();
332 }
333 };
334
335 template <print_iterator OutIter, std::ranges::forward_range Range>
336 OutIter print(
337 OutIter out,
338 Range&& range, // NOLINT(cppcoreguidelines-missing-std-forward)
339 const priority_tag<2>
340 )
341 {
342 out = format::format_to(
343 std::move(out),
344 "{{ ");
345 auto iter = std::ranges::begin(range);
346 if (const auto end = std::ranges::end(range);
347 iter != end)
348 {
349 constexpr PrintFn print{};
350 out = print(std::move(out), *iter++);
351
352 for (; iter != end; ++iter)
353 {
354 out = print(
355 format::format_to(std::move(out), ", "),
356 *iter);
357 }
358 }
359
360 return format::format_to(
361 std::move(out),
362 " }}");
363 }
364
365 template <>
366 class Printer<std::source_location>
367 {
368 public:
369 template <print_iterator OutIter>
370 static OutIter print(OutIter out, const std::source_location& loc)
371 {
372 return format::format_to(
373 std::move(out),
374 "{}[{}:{}], {}",
375 loc.file_name(),
376 loc.line(),
377 loc.column(),
378 loc.function_name());
379 }
380 };
381
382 template <>
383 class Printer<std::nullopt_t>
384 {
385 public:
386 template <print_iterator OutIter>
387 static OutIter print(OutIter out, [[maybe_unused]] const std::nullopt_t)
388 {
389 return format::format_to(
390 std::move(out),
391 "nullopt");
392 }
393 };
394
395 template <typename T>
396 class Printer<std::optional<T>>
397 {
398 public:
399 template <print_iterator OutIter>
400 static OutIter print(OutIter out, const std::optional<T>& opt)
401 {
402 constexpr PrintFn print{};
403
404 if (opt)
405 {
406 out = format::format_to(std::move(out), "{{ value: ");
407 out = print(std::move(out), *opt);
408 return format::format_to(std::move(out), " }}");
409 }
410 return print(std::move(out), std::nullopt);
411 }
412 };
413
414 template <std::size_t index, print_iterator OutIter, typename Tuple>
415 OutIter tuple_element_print(OutIter out, Tuple&& tuple)
416 {
417 if constexpr (0u != index)
418 {
419 out = format::format_to(std::move(out), ", ");
420 }
421
422 constexpr PrintFn printer{};
423 return printer(
424 std::move(out),
425 std::get<index>(std::forward<Tuple>(tuple)));
426 }
427
428 template <typename T>
429 requires requires
430 {
431 typename std::tuple_size<T>::type;
432 requires std::convertible_to<typename std::tuple_size<T>::type, std::size_t>;
433 requires 0u <= std::tuple_size_v<T>;
434 }
435 class Printer<T>
436 {
437 public:
438 template <print_iterator OutIter>
439 static OutIter print(OutIter out, const T& tuple)
440 {
441 out = format::format_to(std::move(out), "{{ ");
442
443 std::invoke(
444 [&]<std::size_t... indices>([[maybe_unused]] const std::index_sequence<indices...>)
445 {
446 (...,
447 (out = tuple_element_print<indices>(std::move(out), tuple)));
448 },
449 std::make_index_sequence<std::tuple_size_v<T>>{});
450
451 return format::format_to(std::move(out), " }}");
452 }
453 };
454
455 template <typename T>
456 concept pointer_like = std::is_pointer_v<T>
457 || std::same_as<std::nullptr_t, T>;
458
459 template <pointer_like T>
460 requires (!string<T>)
461 class Printer<T>
462 {
463 public:
464 template <print_iterator OutIter>
465 static OutIter print(OutIter out, T ptr)
466 {
467 return format::format_to(
468 std::move(out),
469 "0x{:0>16x}",
470 std::bit_cast<std::uintptr_t>(ptr));
471 }
472 };
473
474 template <string String>
475 class Printer<String>
476 {
477 public:
478 template <std::common_reference_with<String> T, print_iterator OutIter>
479 static OutIter print(OutIter out, T&& str)
480 {
481 if constexpr (constexpr auto prefix = string_literal_prefix<string_char_t<String>>;
482 !std::ranges::empty(prefix))
483 {
484 out = out = format::format_to(
485 std::move(out),
486 "{}",
487 prefix);
488 }
489
490 out = format::format_to(std::move(out), "\"");
491
492 // By definition, the string concepts requires the view type to be sized and contiguous.
493 // For simplicity, let's always manually convert the view type to an actual std::string_view,
494 // which is formattable for sure.
495 if constexpr (std::same_as<CharT, string_char_t<String>>)
496 {
497 auto view = string_traits<String>::view(str);
498 out = format::format_to(
499 std::move(out),
500 "{}",
502 std::ranges::data(view),
503 std::ranges::size(view)
504 });
505 }
506 // required for custom char types
507 else if constexpr (printer_for<custom::Printer<string_char_t<String>>, OutIter, string_char_t<String>>)
508 {
509 for (custom::Printer<string_char_t<String>> printer{};
510 const string_char_t<String>& c : string_traits<String>::view(str))
511 {
512 out = printer.print(std::move(out), c);
513 }
514 }
515 else
516 {
517 constexpr auto to_dump = [](const string_char_t<String>& c) noexcept
518 {
519 using intermediate_t = uint_with_size_t<sizeof c>;
520 return std::bit_cast<intermediate_t>(c);
521 };
522
523
524 auto view = string_traits<std::remove_cvref_t<T>>::view(std::forward<T>(str));
525 auto iter = std::ranges::begin(view);
526 if (const auto end = std::ranges::end(view);
527 iter != end)
528 {
529 out = format::format_to(
530 std::move(out),
531 "{:#x}",
532 to_dump(*iter++));
533
534 for (; iter != end; ++iter)
535 {
536 out = format::format_to(
537 std::move(out),
538 ", {:#x}",
539 to_dump(*iter));
540 }
541 }
542 }
543
544 return format::format_to(std::move(out), "\"");
545 }
546 };
547}
548
549namespace mimicpp
550{
597 [[maybe_unused]] inline constexpr detail::PrintFn print{};
598
602}
603
604#endif
User may add specializations, which will then be used during print calls.
Definition Printer.hpp:231
Definition Printer.hpp:44
Definition Printer.hpp:47
constexpr detail::PrintFn print
Functional object, converting the given object to its textual representation.
Definition Printer.hpp:597
constexpr StringViewT string_literal_prefix
Primary template, yielding an empty string.
Definition String.hpp:198
typename string_traits< std::remove_cvref_t< T > >::char_t string_char_t
Computes the character type for the given string.
Definition String.hpp:260
typename uint_with_size< byteCount >::type uint_with_size_t
Convenience constant, exposing the value member of the actual type-trait.
Definition Fwd.hpp:162
Definition Matcher.hpp:26
Definition Printer.hpp:57
Definition BoostTest.hpp:20
char CharT
Definition Fwd.hpp:205
ValueCategory
Definition Fwd.hpp:195
std::basic_ostringstream< CharT, CharTraitsT > StringStreamT
Definition Printer.hpp:41
Constness
Definition Fwd.hpp:188
std::basic_string_view< CharT, CharTraitsT > StringViewT
Definition Fwd.hpp:208
std::basic_string< CharT, CharTraitsT > StringT
Definition Fwd.hpp:207
auto format(const ConstnessT category, auto &ctx) const
Definition Printer.hpp:201
auto format(const ValueCategoryT category, auto &ctx) const
Definition Printer.hpp:172
Definition Utility.hpp:32