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