jaulib v1.5.0
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
string_cfmt2.hpp
Go to the documentation of this file.
1/**
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2024 Gothel Software e.K.
4 *
5 * ***
6 *
7 * SPDX-License-Identifier: MIT
8 *
9 * This Source Code Form is subject to the terms of the MIT License
10 * If a copy of the MIT was not distributed with this
11 * file, You can obtain one at https://opensource.org/license/mit/.
12 *
13 */
14
15#ifndef JAU_STRING_CFMT2_HPP_
16#define JAU_STRING_CFMT2_HPP_
17
18#include <sys/types.h>
19#include <cassert>
20#include <cerrno>
21#include <cstddef>
22#include <cstdio>
23#include <cstdlib>
24#include <cstring>
25#include <limits>
26#include <string>
27#include <string_view>
28#include <type_traits>
29#include <iostream>
30
31#include <jau/byte_util.hpp>
32#include <jau/cpp_lang_util.hpp>
35
36#include <jau/cpp_pragma.hpp>
37
38namespace jau::cfmt2 {
39
40 enum class pstate_t {
41 error,
42 outside,
43 start,
46 };
47 constexpr static const char *to_string(pstate_t s) noexcept {
48 switch( s ) {
49 case pstate_t::outside: return "outside";
50 case pstate_t::start: return "start";
51 case pstate_t::field_width: return "width";
52 case pstate_t::precision: return "precision";
53 default: return "error";
54 }
55 }
56 enum class plength_t {
57 hh,
58 h,
59 none,
60 l,
61 ll,
62 j,
63 z,
64 t,
65 L
66 };
67 constexpr static const char *to_string(plength_t s) noexcept {
68 switch( s ) {
69 case plength_t::hh: return "hh";
70 case plength_t::h: return "h";
71 case plength_t::none: return "";
72 case plength_t::l: return "l";
73 case plength_t::ll: return "ll";
74 case plength_t::j: return "j";
75 case plength_t::z: return "z";
76 case plength_t::t: return "t";
77 case plength_t::L: return "L";
78 default: return "n/a";
79 }
80 }
81
82 static constexpr bool isDigit(const char c) noexcept { return '0' <= c && c <= '9'; }
83
84 namespace impl {
85 template <typename T>
86 class Parser; // fwd
87 }
88
89 struct PResult {
90 const std::string_view fmt;
91 const size_t pos;
92 const ssize_t arg_count;
93 const int line;
94 const pstate_t state;
95 const plength_t length_mod;
96 const bool precision_set;
97
98 template <size_t N>
99 constexpr PResult(const char (&fmt_)[N]) noexcept
100 : fmt(fmt_, N), pos(0), arg_count(0), line(0), state(pstate_t::outside), length_mod(plength_t::none), precision_set(false) { }
101
102 constexpr PResult(const std::string_view fmt_) noexcept
103 : fmt(fmt_), pos(0), arg_count(0), line(0), state(pstate_t::outside), length_mod(plength_t::none), precision_set(false) { }
104
105 constexpr PResult(const PResult &pre) noexcept = default;
106
107 constexpr PResult &operator=(const PResult &x) noexcept = delete;
108
109 constexpr bool hasNext() const noexcept {
110 return !error() && pos < fmt.length() - 1;
111 }
112
113 constexpr ssize_t argCount() const noexcept { return arg_count; }
114 constexpr bool error() const noexcept { return pstate_t::error == state; }
115 constexpr char sym() const noexcept { return fmt[pos]; }
116
117 std::string toString() const {
118 const char c = pos < fmt.length() ? fmt[pos] : '@';
119 std::string s = "args ";
120 s.append(std::to_string(arg_count))
121 .append(", state ")
122 .append(to_string(state))
123 .append(", line ")
124 .append(std::to_string(line))
125 .append(", pos ")
126 .append(std::to_string(pos))
127 .append(", char `")
128 .append(std::string(1, c))
129 .append("`, length `")
130 .append(to_string(length_mod))
131 .append("`, precision ")
132 .append(std::to_string(precision_set))
133 .append(", fmt `")
134 .append(fmt)
135 .append("`");
136 return s;
137 }
138
139 private:
140 template <typename T>
141 friend class impl::Parser;
142
143 constexpr PResult(const PResult &pre, size_t pos2) noexcept
144 : fmt(pre.fmt), pos(pos2), arg_count(pre.arg_count), line(pre.line), state(pre.state), length_mod(pre.length_mod), precision_set(pre.precision_set) {}
145
146 constexpr PResult(const PResult &pre, size_t pos2, ssize_t arg_count2) noexcept
147 : fmt(pre.fmt), pos(pos2), arg_count(arg_count2), line(pre.line), state(pre.state), length_mod(pre.length_mod), precision_set(pre.precision_set) {}
148
149 constexpr PResult(const PResult &pre, pstate_t state2) noexcept
150 : fmt(pre.fmt), pos(pre.pos), arg_count(pre.arg_count), line(pre.line), state(state2), length_mod(pre.length_mod), precision_set(pre.precision_set) {}
151
152 constexpr PResult(const PResult &pre, pstate_t state2, size_t pos2) noexcept
153 : fmt(pre.fmt), pos(pos2), arg_count(pre.arg_count), line(pre.line), state(state2), length_mod(pre.length_mod), precision_set(pre.precision_set) {}
154
155 constexpr PResult(const PResult &pre, pstate_t state2, size_t pos2, ssize_t arg_count2, int line2) noexcept
156 : fmt(pre.fmt), pos(pos2), arg_count(arg_count2), line(line2), state(state2), length_mod(pre.length_mod), precision_set(pre.precision_set) {}
157
158 constexpr PResult(const PResult &pre, plength_t length_mod2, bool precision_set2) noexcept
159 : fmt(pre.fmt), pos(pre.pos), arg_count(pre.arg_count), line(pre.line), state(pre.state), length_mod(length_mod2), precision_set(precision_set2) {}
160
161 constexpr PResult nextSymbol() const noexcept {
162 if( pos < fmt.length() - 1 ) {
163 return PResult(*this, pos+1);
164 } else {
165 return *this;
166 }
167 }
168 constexpr PResult toConversion() const noexcept {
169 if( pstate_t::outside != state ) {
170 return PResult(*this); // inside conversion specifier
171 } else if( fmt[pos] == '%' ) {
172 return PResult(*this, pstate_t::start, pos); // just at start of conversion specifier
173 } else {
174 // seek next conversion specifier
175 const size_t q = fmt.find('%', pos + 1);
176 if( q == std::string::npos ) {
177 // no conversion specifier found
178 return PResult(*this, fmt.length());
179 } else {
180 // new conversion specifier found
181 return PResult(*this, pstate_t::start, q);
182 }
183 }
184 }
185
186 constexpr PResult setError(int l) const noexcept {
187 ssize_t arg_count2;
188 if( 0 == arg_count ) {
189 arg_count2 = std::numeric_limits<ssize_t>::min();
190 } else if( 0 < arg_count ) {
191 arg_count2 = arg_count * -1;
192 } else {
193 arg_count2 = arg_count;
194 }
195 return PResult(*this, pstate_t::error, pos, arg_count2, l);
196 }
197 };
198
199 inline std::ostream &operator<<(std::ostream &out, const PResult &pc) {
200 out << pc.toString();
201 return out;
202 }
203
204 namespace impl {
205
206 static constexpr bool verbose_error = true;
207
208 enum class no_type_t {};
209
210 template <typename T>
211 class Parser {
212 public:
213 constexpr Parser() noexcept = delete;
214
215 /**
216 * Parse the given argument against the current conversion specifier of the format string.
217 *
218 * Multiple rounds of parsing calls might be required, each passing the next argument or null.
219 *
220 * Parsing is completed when method returns false, either signaling an error() or completion.
221 *
222 * @tparam T The type of the given argument
223 * @return true if no error _and_ not complete, i.e. further calls with subsequent parameter required. Otherwise parsing is done due to error or completeness.
224 */
225 static constexpr const PResult parseOne(const PResult pc) noexcept {
226 if( !pc.hasNext() ) {
227 return pc; // done or error
228 }
229
230 const PResult pc2 = pc.toConversion();
231 if( !pc2.hasNext() ) {
232 return pc2; // done
233 }
234
235 // pstate_t::outside != _state
236
237 /* skip '%' or previous `*` */
238 const PResult pc3 = pc2.nextSymbol();
239
240 if( pstate_t::start == pc3.state ) {
241 const PResult pc4 = parseFlags(PResult(pc3, pstate_t::field_width));
242
243 /* parse field width */
244 bool next_arg = false;
245 const PResult pc5 = parseFieldWidth(pc4, next_arg);
246 if( next_arg || pc5.error() ) {
247 return pc5; // error or continue with next argument for same conversion -> field_width
248 }
249 return parseP2(pc5);
250 } else {
251 return parseP2(pc3);
252 }
253 }
254
255 private:
256
257 static constexpr const PResult parseP2(const PResult pc) noexcept {
258 if( pstate_t::field_width == pc.state ) {
259 /* parse precision */
260 const PResult pc2 = PResult(pc, pstate_t::precision);
261 if( pc2.sym() == '.' ) {
262 const PResult pc3 = PResult(pc2, pc2.length_mod, true);
263 bool next_arg = false;
264 const PResult pc4 = parsePrecision(pc3, next_arg);
265 if( next_arg || pc4.error() ) {
266 return pc4; // error or continue with next argument for same conversion -> precision
267 }
268 return parseP3(pc4);
269 } else {
270 return parseP3(pc2);
271 }
272 } else {
273 return parseP3(pc);
274 }
275 }
276
277 static constexpr const PResult parseP3(const PResult pc) noexcept {
278 const PResult pc2 = parseLengthMods(pc);
279 if( pc2.error() ) {
280 return pc2; // error
281 }
282
283 const PResult pc3 = parseFmtSpec(pc2);
284 if( pc3.error() ) {
285 return pc3; // error
286 }
287
288 // next conversion specifier
289 const PResult pc4 = PResult(pc3, pstate_t::outside);
290 if( !pc4.hasNext() ) {
291 return pc4; // done
292 }
293 return PResult(pc4.nextSymbol(), plength_t::none, false); // clear length_mod and precision_set
294 }
295
296 static constexpr const PResult parseFlags(const PResult pc) noexcept {
297 switch( pc.sym() ) {
298 case '0': break;
299 case '-': break;
300 case '+': break;
301 case ' ': break;
302 case '#': break;
303 case '\'': break;
304 default: return pc; // done, not a flag
305 }
306 if( pc.hasNext() ) {
307 return parseFlags(pc.nextSymbol());
308 } else {
309 return pc;
310 }
311 }
312
313 static constexpr const PResult parseDigit(const PResult pc) noexcept {
314 if( !pc.hasNext() || !isDigit(pc.sym()) ) { return pc; }
315 return parseDigit( pc.nextSymbol() );
316 }
317
318 /* parse field width, returns true if parsing can continue or false if next argument is required or error */
319 static constexpr const PResult parseFieldWidth(const PResult pc, bool& next_arg) noexcept {
320 next_arg = false;
321 if( pc.sym() == '*' ) {
322 if( !pc.hasNext() ) { return pc; }
323 const PResult pc2 = pc.nextSymbol();
324
325 using U = std::remove_cv_t<T>;
326
327 if constexpr( std::is_same_v<no_type_t, T> ) {
328 return pc2.setError(__LINE__);
329 }
330 const PResult pc3 = PResult(pc2, pc.arg_count+1);
331 if constexpr( !std::is_same_v<int, U> ) {
332 return pc3.setError(__LINE__);
333 }
334 next_arg = true;
335 return pc3; // next argument is required
336 } else {
337 // continue with current argument
338 return parseDigit(pc);
339 }
340 }
341
342 /* parse precision, returns true if parsing can continue or false if next argument is required or error */
343 static constexpr const PResult parsePrecision(const PResult pc, bool &next_arg) noexcept {
344 next_arg = false;
345 if( !pc.hasNext() ) { return pc; }
346 const PResult pc2 = pc.nextSymbol();
347 const char c = pc.fmt[pc.pos];
348 if( c == '*' ) {
349 if( !pc2.hasNext() ) { return pc2; }
350 const PResult pc3 = pc2.nextSymbol();
351
352 using U = std::remove_cv_t<T>;
353
354 if constexpr( std::is_same_v<no_type_t, T> ) {
355 return pc3.setError(__LINE__);
356 }
357 const PResult pc4 = PResult(pc3, pc.arg_count+1);
358 if constexpr( !std::is_same_v<int, U> ) {
359 return pc4.setError(__LINE__);
360 }
361 next_arg = true;
362 return pc4; // next argument is required
363 } else {
364 // continue with current argument
365 return parseDigit(pc2);
366 }
367 }
368
369 static constexpr const PResult parseLengthMods(const PResult pc) noexcept {
370 const char sym = pc.sym();
371 if( 'h' == sym ) {
372 if( !pc.hasNext() ) { return pc.setError(__LINE__); }
373 const PResult pc2 = pc.nextSymbol();
374 if( 'h' == pc2.sym() ) {
375 if( !pc2.hasNext() ) { return pc2.setError(__LINE__); }
376 return PResult(pc2.nextSymbol(), plength_t::hh, pc2.precision_set);
377 } else {
378 return PResult(pc2, plength_t::h, pc2.precision_set);
379 }
380 } else if( 'l' == sym ) {
381 if( !pc.hasNext() ) { return pc.setError(__LINE__); }
382 const PResult pc2 = pc.nextSymbol();
383 if( 'l' == pc2.sym() ) {
384 if( !pc2.hasNext() ) { return pc2.setError(__LINE__); }
385 return PResult(pc2.nextSymbol(), plength_t::ll, pc2.precision_set);
386 } else {
387 return PResult(pc2, plength_t::l, pc2.precision_set);
388 }
389 } else if( 'j' == sym ) {
390 if( !pc.hasNext() ) { return pc.setError(__LINE__); }
391 return PResult(pc.nextSymbol(), plength_t::j, pc.precision_set);
392 } else if( 'z' == sym ) {
393 if( !pc.hasNext() ) { return pc.setError(__LINE__); }
394 return PResult(pc.nextSymbol(), plength_t::z, pc.precision_set);
395 } else if( 't' == sym ) {
396 if( !pc.hasNext() ) { return pc.setError(__LINE__); }
397 return PResult(pc.nextSymbol(), plength_t::t, pc.precision_set);
398 } else if( 'L' == sym ) {
399 if( !pc.hasNext() ) { return pc.setError(__LINE__); }
400 return PResult(pc.nextSymbol(), plength_t::L, pc.precision_set);
401 } else {
402 return PResult(pc, plength_t::none, pc.precision_set);
403 }
404 }
405
406 static constexpr const PResult parseFmtSpec(const PResult pc) noexcept {
407 const char fmt_literal = unaliasFmtSpec( pc.sym() );
408
409 switch( fmt_literal ) {
410 case '%':
411 case 'c':
412 case 's':
413 return parseStringFmtSpec(pc, fmt_literal);
414 case 'p':
415 return parseAPointerFmtSpec(pc);
416 case 'd':
417 return parseSignedFmtSpec(pc);
418 case 'o':
419 case 'x':
420 case 'X':
421 case 'u':
422 return parseUnsignedFmtSpec(pc, fmt_literal);
423 case 'f':
424 case 'e':
425 case 'E':
426 case 'a':
427 case 'A':
428 case 'g':
429 case 'G':
430 return parseFloatFmtSpec(pc, fmt_literal);
431 default:
432 return pc.setError(__LINE__);
433 } // switch( fmt_literal )
434 }
435
436 static constexpr char unaliasFmtSpec(const char fmt_literal) noexcept {
437 switch( fmt_literal ) {
438 case 'i': return 'd';
439 case 'F': return 'f';
440 default: return fmt_literal;
441 }
442 }
443
444 static constexpr const PResult parseStringFmtSpec(const PResult pc, const char fmt_literal) noexcept {
445 if constexpr( std::is_same_v<no_type_t, T> ) {
446 return pc.setError(__LINE__);
447 }
448 switch( fmt_literal ) {
449 case '%':
450 break;
451 case 'c': {
452 const PResult pc2 = PResult(pc, pc.pos, pc.arg_count+1);
453 using U = std::remove_cv_t<T>;
454 switch( pc2.length_mod ) {
455 case plength_t::none:
456 if constexpr( !std::is_same_v<char, U> ||
457 !std::is_same_v<int, U> ) {
458 return pc2.setError(__LINE__);
459 }
460 break;
461 case plength_t::l:
462 if constexpr( !std::is_same_v<wchar_t, U> ||
463 !std::is_same_v<wint_t, U> ) {
464 return pc2.setError(__LINE__);
465 }
466 break;
467 default:
468 return pc2.setError(__LINE__);
469 }
470 return pc2;
471 }
472 case 's': {
473 const PResult pc2 = PResult(pc, pc.pos, pc.arg_count+1);
474 switch( pc2.length_mod ) {
475 case plength_t::none:
476 if constexpr( !std::is_pointer_v<T> ||
477 !std::is_same_v<char, std::remove_cv_t<std::remove_pointer_t<T>>> ) {
478 return pc2.setError(__LINE__);
479 }
480 break;
481 case plength_t::l:
482 if constexpr( !std::is_pointer_v<T> ||
483 !std::is_same_v<wchar_t, std::remove_cv_t<std::remove_pointer_t<T>>> ) {
484 return pc2.setError(__LINE__);
485 }
486 break;
487 default:
488 return pc2.setError(__LINE__);
489 }
490 return pc2;
491 }
492 default: return pc.setError(__LINE__);
493 }
494 return pc;
495 }
496
497 static constexpr const PResult parseAPointerFmtSpec(const PResult pc) noexcept {
498 const PResult pc2 = PResult(pc, plength_t::none, pc.precision_set);
499 if constexpr( std::is_same_v<no_type_t, T> ) {
500 return pc2.setError(__LINE__);
501 }
502 const PResult pc3 = PResult(pc2, pc2.pos, pc2.arg_count+1);
503 if constexpr( !std::is_pointer_v<T> ) {
504 return pc3.setError(__LINE__);
505 }
506 return pc3;
507 }
508
509 static constexpr const PResult parseSignedFmtSpec(const PResult pc) noexcept {
510 if constexpr( std::is_same_v<no_type_t, T> ) {
511 return pc.setError(__LINE__);
512 }
513 const PResult pc2 = PResult(pc, pc.pos, pc.arg_count+1);
514
515 using U = std::remove_cv_t<T>;
516 // using U = std::conditional<std::is_integral_v<T> && std::is_unsigned_v<T>, std::make_signed<T>, T>::type; // triggers the instantiating the 'other' case and hence fails
517 using V = typename std::conditional_t<std::is_integral_v<U> && std::is_unsigned_v<U>, std::make_signed<U>, std::type_identity<U>>::type; // NOLINT
518
519 switch( pc2.length_mod ) {
520 case plength_t::hh:
521 if constexpr( !std::is_same_v<char, V> && (!std::is_integral_v<V> || sizeof(V) > sizeof(char)) ) {
522 return pc2.setError(__LINE__);
523 }
524 break;
525 case plength_t::h:
526 if constexpr( !std::is_same_v<short, V> && (!std::is_integral_v<V> || sizeof(V) > sizeof(short)) ) {
527 return pc2.setError(__LINE__);
528 }
529 break;
530 case plength_t::none:
531 if constexpr( !std::is_same_v<int, V> && (!std::is_integral_v<V> || sizeof(V) > sizeof(int)) ) {
532 return pc2.setError(__LINE__);
533 }
534 break;
535 case plength_t::l:
536 if constexpr( !std::is_same_v<long, V> && (!std::is_integral_v<V> || sizeof(V) > sizeof(long)) ) {
537 return pc2.setError(__LINE__);
538 }
539 break;
540 case plength_t::ll:
541 if constexpr( !std::is_same_v<long long, V> && (!std::is_integral_v<V> || sizeof(V) > sizeof(long long)) ) {
542 return pc2.setError(__LINE__);
543 }
544 break;
545 case plength_t::j:
546 if constexpr( !std::is_same_v<intmax_t, V> && (!std::is_integral_v<V> || sizeof(V) > sizeof(intmax_t)) ) {
547 return pc2.setError(__LINE__);
548 }
549 break;
550 case plength_t::z:
551 if constexpr( !std::is_same_v<ssize_t, V> && (!std::is_integral_v<V> || sizeof(V) > sizeof(ssize_t)) ) {
552 return pc2.setError(__LINE__);
553 }
554 break;
555 case plength_t::t:
556 if constexpr( !std::is_same_v<ptrdiff_t, V> && (!std::is_integral_v<V> || sizeof(V) > sizeof(ptrdiff_t)) ) {
557 return pc2.setError(__LINE__);
558 }
559 break;
560 default:
561 return pc2.setError(__LINE__);
562 }
563 return pc2;
564 }
565
566 static constexpr const PResult parseUnsignedFmtSpec(const PResult pc, const char /*fmt_literal*/) noexcept {
567 if constexpr( std::is_same_v<no_type_t, T> ) {
568 return pc.setError(__LINE__);
569 }
570 const PResult pc2 = PResult(pc, pc.pos, pc.arg_count+1);
571
572 using U = std::remove_cv_t<T>;
573 // using U = std::conditional_t<std::is_integral_v<T> && std::is_signed_v<T>, std::make_unsigned_t<T>, T>; // triggers the instantiating the 'other' case and hence fails
574 using V = typename std::conditional_t<std::is_integral_v<U> && std::is_signed_v<U>, std::make_unsigned<U>, std::type_identity<U>>::type; // NOLINT
575
576 switch( pc2.length_mod ) {
577 case plength_t::hh:
578 if constexpr( !std::is_same_v<unsigned char, V> && (!std::is_integral_v<V> || sizeof(V) > sizeof(unsigned char)) ) {
579 return pc2.setError(__LINE__);
580 }
581 break;
582 case plength_t::h:
583 if constexpr( !std::is_same_v<unsigned short, V> && (!std::is_integral_v<V> || sizeof(V) > sizeof(unsigned short)) ) {
584 return pc2.setError(__LINE__);
585 }
586 break;
587 case plength_t::none:
588 if constexpr( !std::is_same_v<unsigned int, V> && (!std::is_integral_v<V> || sizeof(V) > sizeof(unsigned int)) ) {
589 return pc2.setError(__LINE__);
590 }
591 break;
592 case plength_t::l:
593 if constexpr( !std::is_same_v<unsigned long, V> && (!std::is_integral_v<V> || sizeof(V) > sizeof(unsigned long)) ) {
594 return pc2.setError(__LINE__);
595 }
596 break;
597 case plength_t::ll:
598 if constexpr( !std::is_same_v<unsigned long long, V> && (!std::is_integral_v<V> || sizeof(V) > sizeof(unsigned long long)) ) {
599 return pc2.setError(__LINE__);
600 }
601 break;
602 case plength_t::j:
603 if constexpr( !std::is_same_v<uintmax_t, V> && (!std::is_integral_v<V> || sizeof(V) > sizeof(uintmax_t)) ) {
604 return pc2.setError(__LINE__);
605 }
606 break;
607 case plength_t::z:
608 if constexpr( !std::is_same_v<size_t, V> && (!std::is_integral_v<V> || sizeof(V) > sizeof(size_t)) ) {
609 return pc2.setError(__LINE__);
610 }
611 break;
612#if 0
613 case plength_t::t:
614 if constexpr( !std::is_same_v<unsigned ptrdiff_t, U> ) {
615 return pc2.setError(__LINE__);
616 }
617 break;
618#endif
619 default:
620 return pc2.setError(__LINE__);
621 }
622 return pc2;
623 }
624
625 static constexpr const PResult parseFloatFmtSpec(const PResult pc, const char /*fmt_literal*/) noexcept {
626 if constexpr( std::is_same_v<no_type_t, T> ) {
627 return pc.setError(__LINE__);
628 }
629 const PResult pc2 = PResult(pc, pc.pos, pc.arg_count+1);
630
631 using U = std::remove_cv_t<T>;
632
633 switch( pc2.length_mod ) {
634 case plength_t::none:
635 case plength_t::l:
636 if constexpr( !std::is_same_v<float, U> &&
637 !std::is_same_v<double, U> ) {
638 return pc2.setError(__LINE__);
639 }
640 break;
641 case plength_t::L:
642 if constexpr( !std::is_same_v<float, U> &&
643 !std::is_same_v<double, U> &&
644 !std::is_same_v<long double, U> ) {
645 } else {
646 return pc2.setError(__LINE__);
647 }
648 break;
649 default:
650 return pc2.setError(__LINE__);
651 }
652 return pc2;
653 }
654 };
655
656 constexpr const PResult checkRec(const PResult ctx) noexcept {
657 return Parser<no_type_t>::parseOne(ctx);
658 }
659
660 template <typename Targ, typename... Tnext>
661 constexpr const PResult checkRec(const PResult ctx) noexcept {
662 if constexpr( 0 < sizeof...(Tnext) ) {
663 return checkRec<Tnext...>( Parser<Targ>::parseOne(ctx) );
664 } else {
665 return Parser<no_type_t>::parseOne( Parser<Targ>::parseOne(ctx) );
666 }
667 }
668
669 } // namespace impl
670
671 /**
672 * Strict type validation of arguments against the format string.
673 *
674 * See @ref jau_cfmt_header for details
675 *
676 * @tparam Targs the argument template type pack to be validated against the format string
677 * @param fmt the snprintf format string
678 * @param args passed arguments, used for template type deduction only
679 * @return true if successfully parsed format and arguments, false otherwise.
680 * @see @ref jau_cfmt_header
681 */
682 template <typename... Targs>
683 constexpr bool check(const std::string_view fmt, const Targs &...) noexcept {
684 return !impl::checkRec<Targs...>( PResult(fmt) ).error();
685 }
686
687 /**
688 * Strict type validation of arguments against the format string.
689 *
690 * See @ref jau_cfmt_header for details
691 *
692 * @tparam Targs the argument template type pack to be validated against the format string
693 * @param fmt the snprintf format string
694 * @return true if successfully parsed format and arguments, false otherwise.
695 * @see @ref jau_cfmt_header
696 */
697 template <typename... Targs>
698 constexpr bool check2(const std::string_view fmt) noexcept {
699 return !impl::checkRec<Targs...>( PResult(fmt) ).error();
700 }
701
702 /**
703 * Strict type validation of arguments against the format string.
704 *
705 * See @ref jau_cfmt_header for details
706 *
707 * @tparam Targs the argument template type pack to be validated against the format string
708 * @param fmt the snprintf format string
709 * @param args passed arguments, used for template type deduction only
710 * @return PContext result object for further inspection.
711 * @see @ref jau_cfmt_header
712 */
713 template <typename... Targs>
714 constexpr const PResult checkR(const std::string_view fmt, const Targs &...) noexcept {
715 return impl::checkRec<Targs...>( PResult(fmt) );
716 }
717
718 /**
719 * Strict type validation of arguments against the format string.
720 *
721 * See @ref jau_cfmt_header for details
722 *
723 * @tparam Targs the argument template type pack to be validated against the format string
724 * @param fmt the snprintf format string
725 * @return PContext result object for further inspection.
726 * @see @ref jau_cfmt_header
727 */
728 template <typename... Targs>
729 constexpr const PResult checkR2(const std::string_view fmt) noexcept {
730 return impl::checkRec<Targs...>( PResult(fmt) );
731 }
732
733} // namespace jau::cfmt2
734
735#endif // JAU_STRING_CFMT2_HPP_
std::string to_string(const alphabet &v) noexcept
plength_t
Format length modifiers.
consteval_cxx20 ssize_t check2(std::string_view fmt) noexcept
Strict compile-time type validation of explicit argument-types against the format string.
consteval_cxx20 Result checkR(std::string_view fmt, const Targs &...) noexcept
Strict compile-time type validation of deduced argument-types against the format string.
consteval_cxx20 Result checkR2(std::string_view format) noexcept
Strict compile-time type validation of explicit argument-types against the format string.
consteval_cxx20 ssize_t check(std::string_view fmt, const Targs &...) noexcept
Strict compile-time type validation of deduced argument-types against the format string.
@ j
intmax_t or uintmax_t integer
@ ll
long long integer
@ L
long double float
@ z
size_t or ssize_t integer
@ none
Undefined.
Definition cpuid.hpp:52
static std::ostream & operator<<(std::ostream &out, const TestDataBF &v)