mimic++ v2
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 <ranges>
20#include <source_location>
21#include <sstream>
22#include <string>
23#include <string_view>
24#include <utility>
25
26#ifndef MIMICPP_CONFIG_USE_FMT
27 #include <format>
28#else
29
30#if __has_include(<fmt/format.h>)
31#include <fmt/format.h>
32#else
33 #error "The fmt formatting backend is explicitly enabled, but the include <fmt/format.h> can not be found."
34#endif
35
36#endif
37
38namespace mimicpp
39{
40 using StringViewT = std::basic_string_view<CharT, CharTraitsT>;
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<5>
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<4>
262 )
263 {
264 return Printer::print(
265 std::move(out),
266 std::forward<T>(value));
267 }
268
269 template <print_iterator OutIter, std::convertible_to<StringViewT> String>
270 OutIter print(
271 OutIter out,
272 String&& str,
273 [[maybe_unused]] const priority_tag<3>
274 )
275 {
276 return format::format_to(
277 std::move(out),
278 "\"{}\"",
279 static_cast<StringViewT>(std::forward<String>(str)));
280 }
281
282 template <print_iterator OutIter, std::ranges::forward_range Range>
283 OutIter print(
284 OutIter out,
285 Range&& range,
286 priority_tag<2>
287 );
288
289 template <print_iterator OutIter, format::detail::formattable<CharT> T>
290 OutIter print(
291 OutIter out,
292 T&& value,
293 [[maybe_unused]] const priority_tag<1>
294 )
295 {
296 return format::format_to(
297 std::move(out),
298 "{}",
299 std::forward<T>(value));
300 }
301
302 template <print_iterator OutIter>
303 OutIter print(
304 OutIter out,
305 auto&&,
306 [[maybe_unused]] const priority_tag<0>
307 )
308 {
309 return format::format_to(
310 std::move(out),
311 "{{?}}");
312 }
313
314 class PrintFn
315 {
316 public:
317 template <print_iterator OutIter, typename T>
318 OutIter operator ()(
319 OutIter out,
320 T&& value
321 ) const
322 {
323 static_assert(
324 requires(const priority_tag<6> tag)
325 {
326 { print(out, std::forward<T>(value), tag) } -> std::convertible_to<OutIter>;
327 },
328 "The given type is not printable. ");
329
330 return print(
331 std::move(out),
332 std::forward<T>(value),
333 priority_tag<6>{});
334 }
335
336 template <typename T>
337 StringT operator ()(T&& value) const
338 {
339 StringStreamT stream{};
340 std::invoke(
341 *this,
342 std::ostreambuf_iterator{stream},
343 std::forward<T>(value));
344 return std::move(stream).str();
345 }
346 };
347
348 template <print_iterator OutIter, std::ranges::forward_range Range>
349 OutIter print(
350 OutIter out,
351 Range&& range, // NOLINT(cppcoreguidelines-missing-std-forward)
352 const priority_tag<2>
353 )
354 {
355 out = format::format_to(
356 std::move(out),
357 "{{ ");
358 auto iter = std::ranges::begin(range);
359 if (const auto end = std::ranges::end(range);
360 iter != end)
361 {
362 constexpr PrintFn print{};
363 out = print(std::move(out), *iter++);
364
365 for (; iter != end; ++iter)
366 {
367 out = print(
368 format::format_to(std::move(out), ", "),
369 *iter);
370 }
371 }
372
373 return format::format_to(
374 std::move(out),
375 " }}");
376 }
377
378 template <>
379 class Printer<std::source_location>
380 {
381 public:
382 template <print_iterator OutIter>
383 static OutIter print(OutIter out, const std::source_location& loc)
384 {
385 return format::format_to(
386 std::move(out),
387 "{}[{}:{}], {}",
388 loc.file_name(),
389 loc.line(),
390 loc.column(),
391 loc.function_name());
392 }
393 };
394
395 template <typename Char>
396 requires is_character_v<Char>
397 struct character_literal_printer;
398
399 template <>
400 struct character_literal_printer<char>
401 {
402 template <print_iterator OutIter>
403 static OutIter print(OutIter out) noexcept
404 {
405 // no special character-literal
406 return out;
407 }
408 };
409
410 template <>
411 struct character_literal_printer<wchar_t>
412 {
413 template <print_iterator OutIter>
414 static OutIter print(OutIter out)
415 {
416 return format::format_to(std::move(out), "L");
417 }
418 };
419
420 template <>
421 struct character_literal_printer<char8_t>
422 {
423 template <print_iterator OutIter>
424 static OutIter print(OutIter out)
425 {
426 return format::format_to(std::move(out), "u8");
427 }
428 };
429
430 template <>
431 struct character_literal_printer<char16_t>
432 {
433 template <print_iterator OutIter>
434 static OutIter print(OutIter out)
435 {
436 return format::format_to(std::move(out), "u");
437 }
438 };
439
440 template <>
441 struct character_literal_printer<char32_t>
442 {
443 template <print_iterator OutIter>
444 static OutIter print(OutIter out)
445 {
446 return format::format_to(std::move(out), "U");
447 }
448 };
449
450 template <string String>
451 requires (!std::same_as<CharT, string_char_t<String>>)
452 class Printer<String>
453 {
454 public:
455 template <std::common_reference_with<String> T, print_iterator OutIter>
456 static OutIter print(OutIter out, T&& str)
457 {
458 using intermediate_t = std::uint32_t;
459 static_assert(sizeof(string_char_t<String>) <= sizeof(intermediate_t));
460
461 out = character_literal_printer<string_char_t<String>>::print(std::move(out));
462 out = format::format_to(std::move(out), "\"");
463
464 auto view = string_traits<std::remove_cvref_t<T>>::view(std::forward<T>(str));
465 auto iter = std::ranges::begin(view);
466 if (const auto end = std::ranges::end(view);
467 iter != end)
468 {
469 out = format::format_to(
470 std::move(out),
471 "{:#x}",
472 static_cast<intermediate_t>(*iter++));
473
474 for (; iter != end; ++iter)
475 {
476 out = format::format_to(
477 std::move(out),
478 ", {:#x}",
479 static_cast<intermediate_t>(*iter));
480 }
481 }
482
483 return format::format_to(std::move(out), "\"");
484 }
485 };
486}
487
488namespace mimicpp
489{
537 inline constexpr detail::PrintFn print{};
538
542}
543
544#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:537
constexpr bool is_character_v
Convenience boolean-constant to the result of is_character trait.
Definition String.hpp:54
typename string_traits< std::remove_cvref_t< T > >::char_t string_char_t
Computes the character type for the given string.
Definition String.hpp:144
Definition Matcher.hpp:26
Definition Printer.hpp:57
Definition BoostTest.hpp:20
char CharT
Definition Fwd.hpp:110
ValueCategory
Definition Fwd.hpp:100
std::basic_ostringstream< CharT, CharTraitsT > StringStreamT
Definition Printer.hpp:41
Constness
Definition Fwd.hpp:93
std::basic_string_view< CharT, CharTraitsT > StringViewT
Definition Printer.hpp:40
std::basic_string< CharT, CharTraitsT > StringT
Definition Fwd.hpp:112
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