MOS Source Code
Loading...
Searching...
No Matches
formatter.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#pragma once
4
5#include <cstddef>
6#include <limits>
7#include <mos/type_utils.hpp>
8#include <tuple>
9#include <type_traits>
10
11namespace mos
12{
13 struct Specifier
14 {
22
30
37
38 static constexpr auto DYNAMIC = std::numeric_limits<size_t>::max();
39
41
42 size_t width = 0;
43 size_t precision = 0;
44
45 // which one comes first, width or precision?
46 // this will be used at runtime to determine which one to set first
48
50 char filler = '\0';
51 bool debug = false;
52
53 // #, alternative form. For integer specifiers (d, i, o, u, x, X), a non-zero result will have the
54 // string '0x' (or '0X' for 'X' conversions) prepended to it.
55 bool alternative = false;
56
60 consteval size_t n_dynamic_args() const
61 {
62 return (width == DYNAMIC) + (precision == DYNAMIC) + 1;
63 }
64 };
65
67 {
68 private:
69 template<Specifier __spec>
70 struct Formatter
71 {
72 static constexpr auto spec = __spec;
73
74 template<typename TArg, Specifier _config = spec, std::enable_if_t<_config.n_dynamic_args() == 1, int> = 0>
75 static constexpr auto arg_tuple() -> std::tuple<TArg>;
76
77 template<typename TArg, Specifier _config = spec, std::enable_if_t<_config.n_dynamic_args() == 2, int> = 0>
78 static constexpr auto arg_tuple() -> std::tuple<TArg, size_t>;
79
80 template<typename TArg, Specifier _config = spec, std::enable_if_t<_config.n_dynamic_args() == 3, int> = 0>
81 static constexpr auto arg_tuple() -> std::tuple<TArg, size_t, size_t>;
82
83 template<typename TArg, typename Stream>
84 inline Stream &Print(Stream &os, const decltype(arg_tuple<TArg>()) &args)
85 {
86 if constexpr (spec.n_dynamic_args() == 1)
87 {
88 const auto &[arg] = args;
89 os << arg;
90 }
91 else if constexpr (spec.n_dynamic_args() == 2)
92 {
93 const auto &[arg, n1] = args;
94 os << '[' << arg << " " << n1 << ']';
95 }
96 else if constexpr (spec.n_dynamic_args() == 3)
97 {
98 const auto &[arg, n1, n2] = args;
99 os << '[' << arg << " " << n1 << " " << n2 << ']';
100 }
101 return os;
102 }
103 };
104
105 private:
106 template<Specifier orig>
108 {
109 static_assert(orig.alignment == Specifier::FormatAlignment::_DEFAULT, "Alignment already set");
110 Specifier config = orig;
111 config.alignment = alignment;
112 return config;
113 }
114
115 template<Specifier orig>
116 static consteval Specifier SetWidth(size_t width)
117 {
118 Specifier config = orig;
119 config.width = width;
122 return config;
123 }
124
125 template<Specifier orig>
126 static consteval Specifier SetPrecision(size_t precision)
127 {
128 Specifier config = orig;
129 config.precision = precision;
132 return config;
133 }
134
135 template<Specifier orig>
137 {
138 static_assert(orig.sign == Specifier::FormatSign::_DEFAULT, "Sign already set");
139 Specifier config = orig;
140 config.sign = sign;
141 return config;
142 }
143
144 template<Specifier orig>
145 static consteval Specifier SetFiller(char fill)
146 {
147 static_assert(orig.filler == '\0', "Fill already set");
148 Specifier config = orig;
149 config.filler = fill;
150 return config;
151 }
152
153 template<Specifier orig>
154 static consteval Specifier SetDebugFormat(bool debug_format)
155 {
156 static_assert(orig.debug == false, "Debug format already set");
157 Specifier config = orig;
158 config.debug = debug_format;
159 return config;
160 }
161
162 template<Specifier orig>
163 static consteval Specifier SetAlternativeForm(bool alternative_form)
164 {
165 static_assert(orig.alternative == false, "Alternative form already set");
166 Specifier config = orig;
167 config.alternative = alternative_form;
168 return config;
169 }
170
171 private:
172 consteval static auto tuple_prepend(auto a, auto b = std::tuple())
173 {
174 return std::tuple_cat(std::make_tuple(a), b);
175 }
176
177 // clang-format off
178 template<typename T> struct is_formatter : std::false_type {};
179 template<Specifier c> struct is_formatter<Formatter<c>> : std::true_type {};
180 template<typename T> static consteval bool is_formatter_v() { return is_formatter<T>::value; };
181 // clang-format on
182
183 template<typename T>
184 static consteval size_t n_dynamic_args()
185 {
186 if constexpr (is_formatter_v<T>())
187 return T::spec.n_dynamic_args();
188 else
189 return 0;
190 }
191
192 template<typename... Type>
193 static consteval size_t n_args_expected(std::tuple<>)
194 {
195 return 0;
196 }
197
198 template<typename... Type>
199 static consteval size_t n_args_expected(std::tuple<Type...>)
200 {
201 return ((is_formatter_v<Type>() ? n_dynamic_args<Type>() : 0) + ...);
202 }
203
204 template<string_literal view, size_t I, size_t End, size_t Result = 0>
205 static consteval size_t __do_parse_int()
206 {
207 if constexpr (view[I] < '0' || view[I] > '9' || I == view.strlen || I == End)
208 return Result;
209 else
210 return __do_parse_int<view, I + 1, End, Result * 10 + (view[I] - '0')>();
211 }
212
213 template<string_literal view, size_t IStart>
214 static consteval size_t __do_find_integer_boundary()
215 {
216 if constexpr (view[IStart] < '0' || view[IStart] > '9' || IStart == view.strlen)
217 return IStart;
218 else
220 }
221
222 template<string_literal view, size_t I, size_t StartI, Specifier config>
223 static consteval auto do_parse_specifier()
224 {
225 if constexpr (view[I] == '}')
227 else if constexpr (view[I] == '{')
228 {
229 // dynamic width
230 static_assert(view[I + 1] == '}', "Invalid dynamic width specifier");
232 }
233 else if constexpr (view[I] == '.')
234 {
235 if constexpr (view[I + 1] == '{')
236 {
237 // dynamic precision
238 static_assert(view[I + 2] == '}', "Invalid dynamic precision specifier");
240 }
241 else if constexpr (view[I + 1] >= '0' && view[I + 1] <= '9')
242 {
243 constexpr auto end = __do_find_integer_boundary<view, I + 1>();
244 constexpr size_t value = __do_parse_int<view, I + 1, end>();
246 }
247 else
248 {
249 static_assert(false, "Invalid precision specifier");
250 }
251 }
252 else
253 {
254 // continue current parsing
255 if constexpr (view[I] == '<')
257 else if constexpr (view[I] == '>')
259 else if constexpr (view[I] == '^')
261 else if constexpr (view[I] == '+')
262 return do_parse_specifier<view, I + 1, StartI, SetSign<config>(Specifier::FormatSign::BOTH)>();
263 else if constexpr (view[I] == '-')
265 else if constexpr (view[I] == ' ')
267 else if constexpr (view[I] == '?')
268 return do_parse_specifier<view, I + 1, StartI, SetDebugFormat<config>(true)>();
269 else if constexpr (view[I] == '#')
270 return do_parse_specifier<view, I + 1, StartI, SetAlternativeForm<config>(true)>();
271 else if constexpr (view[I] == ':')
273 else if constexpr (view[I] >= '0' && view[I] <= '9')
274 {
275 constexpr auto end = __do_find_integer_boundary<view, I>();
276 constexpr size_t value = __do_parse_int<view, I, end>();
278 }
279 else
281 }
282 }
283
284 private:
285 template<size_t StartI, size_t N, typename... Args>
286 static inline constexpr auto get_slice(const std::tuple<Args...> &args)
287 {
288 if constexpr (StartI == N || StartI >= std::tuple_size_v<std::remove_cvref_t<decltype(args)>>)
289 return std::tuple();
290 else
291 return std::tuple_cat(std::make_tuple(std::get<StartI>(args)), get_slice<StartI + 1, N>(args));
292 }
293
294 template<typename... TArgs>
295 static inline constexpr auto first_arg(const std::tuple<TArgs...> &args)
296 {
297 return std::get<0>(args);
298 }
299
300 template<size_t N, typename... TArgs>
301 static inline constexpr auto first_n_args(const std::tuple<TArgs...> &args)
302 {
303 return get_slice<0, N>(args);
304 }
305
306 template<typename... TArgs>
307 static inline constexpr auto rest_args(const std::tuple<TArgs...> &args)
308 {
309 return get_slice<1, std::tuple_size_v<std::remove_cvref_t<decltype(args)>>>(args);
310 }
311
312 template<size_t N, typename... TArgs>
313 static inline constexpr auto rest_args_from(const std::tuple<TArgs...> &args)
314 {
315 return get_slice<N, std::tuple_size_v<std::remove_cvref_t<decltype(args)>>>(args);
316 }
317
318 template<typename Stream, typename... TArgs>
319 static inline constexpr auto do_next_print(Stream &stream, std::tuple<TArgs...> str_format_tuple)
320 {
321 constexpr auto rest_specifier_size = std::tuple_size_v<std::remove_cvref_t<decltype(str_format_tuple)>>;
322 static_assert(rest_specifier_size == 0 || rest_specifier_size == 1, "Invalid format string");
323
324 if constexpr (rest_specifier_size == 1)
325 stream << first_arg(str_format_tuple);
326 }
327
328 template<typename Stream, typename... TSpecs, typename TSpec, typename TArg, typename... TArgs>
329 static inline constexpr auto do_next_print(Stream &stream, TSpec spec, const std::tuple<TSpecs...> &specs, const TArg &arg, const std::tuple<TArgs...> &args)
330 {
331 constexpr auto rest_specifier_size = std::tuple_size_v<std::remove_cvref_t<decltype(specs)>>;
332 constexpr auto rest_arg_size = std::tuple_size_v<std::remove_cvref_t<decltype(args)>>;
333
334 if constexpr (std::is_same_v<TSpec, mos::string_view>)
335 {
336 stream << spec;
337 do_next_print(stream, first_arg(specs), rest_args(specs), arg, args); // print string, keeps args
338 }
339 else if constexpr (is_formatter_v<TSpec>())
340 {
341 using TArgTuple = decltype(TSpec::template arg_tuple<TArg>());
342 constexpr auto TArgTupleSize = std::tuple_size_v<TArgTuple>;
343 static_assert(TArgTupleSize == 1 || TArgTupleSize == 2 || TArgTupleSize == 3, "Invalid number of arguments");
344
345 static_assert(TArgTupleSize - 1 <= rest_arg_size, "Not enough arguments");
346
347 spec.template Print<TArg>(stream, std::tuple_cat(std::make_tuple(arg), first_n_args<TArgTupleSize - 1>(args)));
348
349 if constexpr (rest_arg_size - (TArgTupleSize - 1) > 0)
350 {
351 // print formatter, keeps args
352 do_next_print(stream, first_arg(specs), rest_args(specs), std::get<TArgTupleSize - 1>(args), rest_args_from<TArgTupleSize>(args));
353 }
354 else
355 {
356 static_assert(rest_specifier_size == 0 || rest_specifier_size == 1, "Invalid number of arguments");
357 do_next_print(stream, specs);
358 }
359 }
360 else
361 {
362 static_assert(false, "Invalid specifier");
363 }
364 }
365
366 public:
367 template<string_literal view, size_t I = 0, size_t StartI = 0>
368 static inline consteval auto do_parse_format_string()
369 {
370 if constexpr (I == view.strlen)
371 {
372 if constexpr (StartI == I)
373 return std::make_tuple();
374 else
375 return std::make_tuple(mos::string_view{ view.at(StartI), view.at(I) });
376 }
377 else if constexpr (view[I] == '{' && (I < 1 || view[I - 1] != '\\'))
378 {
379 // remove forward slash if being escaped
380 constexpr mos::string_view string{ view.at(StartI), view.at(I) };
381 if constexpr (StartI == I)
382 return do_parse_specifier<view, I + 1, I, Specifier{}>();
383 else
384 return tuple_prepend(string, do_parse_specifier<view, I + 1, I, Specifier{}>());
385 }
386 else
387 {
388 constexpr bool escaped = view[I] == '{' && (I >= 1 && view[I - 1] == '\\');
390 }
391 }
392
393 template<mos::string_literal view, typename Stream, typename... TArgs>
394 static inline constexpr Stream &print_literal(Stream &stream, TArgs... args)
395 {
396 constexpr auto format = do_parse_format_string<view>();
397 constexpr auto args_expected = n_args_expected(format);
398 static_assert(args_expected == sizeof...(args), "Number of arguments does not match format string");
399
400 if constexpr (args_expected == 0)
401 {
402 do_next_print(stream, format);
403 }
404 else
405 {
406 const auto args_tuple = std::make_tuple(args...);
407 do_next_print(stream, first_arg(format), rest_args(format), first_arg(args_tuple), rest_args(args_tuple));
408 }
409 return stream;
410 }
411
412 template<typename M, typename Stream, typename... TArgs>
413 static inline constexpr Stream &print_m(Stream &stream, std::tuple<TArgs...> args)
414 {
416 constexpr auto args_expected = n_args_expected(format);
417 static_assert(args_expected == sizeof...(TArgs), "Number of arguments does not match format string");
418
419 if constexpr (args_expected == 0)
420 {
421 do_next_print(stream, format);
422 }
423 else
424 {
426 }
427
428 return stream;
429 }
430
432 {
433 };
434 };
435
436} // namespace mos
437
438#define formatted_type(literal) \
439 []() \
440 { \
441 struct M : mos::FormatImpl::formatted_string \
442 { \
443 static consteval auto string_value() \
444 { \
445 return ::mos::string_literal{ literal }; \
446 } \
447 }; \
448 return M{}; \
449 }()
450
451#if 0
452struct FakeStreamType
453{
454 FakeStreamType &operator<<(int);
455 FakeStreamType &operator<<(mos::string_view);
456};
457
458inline auto FakeStream = FakeStreamType();
459
460void testfunc()
461{
462 mos::FormatImpl::print_literal<"\\{ 'TODO: IO.Name', {}}">(FakeStream, 1 == 0 ? "A" : "B");
463}
464#endif
char args[3][16]
Definition avr_io.c:16
MOSAPI const char *__restrict format
Definition mos_stdio.hpp:17
mos::SyslogStreamWriter operator<<(mos::SyslogStreamWriter stream, VMFlags flags)
Definition mm_types.hpp:33
basic_string_view< char > string_view
static constexpr auto arg_tuple() -> std::tuple< TArg >
static constexpr auto arg_tuple() -> std::tuple< TArg, size_t, size_t >
static constexpr auto arg_tuple() -> std::tuple< TArg, size_t >
static constexpr auto spec
Definition formatter.hpp:72
Stream & Print(Stream &os, const decltype(arg_tuple< TArg >()) &args)
Definition formatter.hpp:84
static consteval size_t n_args_expected(std::tuple<>)
static consteval Specifier SetPrecision(size_t precision)
static constexpr Stream & print_m(Stream &stream, std::tuple< TArgs... > args)
static consteval Specifier SetWidth(size_t width)
static constexpr Stream & print_literal(Stream &stream, TArgs... args)
static consteval auto do_parse_specifier()
static constexpr auto rest_args(const std::tuple< TArgs... > &args)
static constexpr auto get_slice(const std::tuple< Args... > &args)
static constexpr auto do_next_print(Stream &stream, std::tuple< TArgs... > str_format_tuple)
static consteval auto tuple_prepend(auto a, auto b=std::tuple())
static consteval Specifier SetSign(Specifier::FormatSign sign)
static constexpr auto do_next_print(Stream &stream, TSpec spec, const std::tuple< TSpecs... > &specs, const TArg &arg, const std::tuple< TArgs... > &args)
static consteval size_t n_dynamic_args()
static consteval auto do_parse_format_string()
static consteval Specifier SetAlignment(Specifier::FormatAlignment alignment)
static constexpr auto rest_args_from(const std::tuple< TArgs... > &args)
static constexpr auto first_arg(const std::tuple< TArgs... > &args)
static consteval Specifier SetAlternativeForm(bool alternative_form)
static consteval Specifier SetFiller(char fill)
static consteval bool is_formatter_v()
static consteval size_t __do_find_integer_boundary()
static consteval size_t __do_parse_int()
static constexpr auto first_n_args(const std::tuple< TArgs... > &args)
static consteval size_t n_args_expected(std::tuple< Type... >)
static consteval Specifier SetDebugFormat(bool debug_format)
@ NEGATIVE
only negative numbers are prefixed with a sign
Definition formatter.hpp:27
@ BOTH
both native and positive numbers are prefixed with a sign
Definition formatter.hpp:26
@ LEADING_SPACE
positive numbers are prefixed with a leading space
Definition formatter.hpp:28
@ _DEFAULT
default, negative
Definition formatter.hpp:25
consteval size_t n_dynamic_args() const
number of runtime arguments required to print this item.
Definition formatter.hpp:60
FormatAlignment alignment
Definition formatter.hpp:40
WidthPrecisionOrder order
Definition formatter.hpp:47
FormatSign sign
Definition formatter.hpp:49
static constexpr auto DYNAMIC
Definition formatter.hpp:38