jaulib v1.3.6
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
string_cfmt.hpp
Go to the documentation of this file.
1/**
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2021 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_CFMT_HPP_
16#define JAU_STRING_CFMT_HPP_
17
18#include <sys/types.h>
19#include <cassert>
20#include <cerrno>
21#include <cstddef>
22#include <cstdint>
23#include <cstdio>
24#include <cstdlib>
25#include <cstring>
26#include <limits>
27#include <string>
28#include <string_view>
29#include <type_traits>
30#include <iostream>
31
32#include <jau/byte_util.hpp>
33#include <jau/cpp_lang_util.hpp>
36
37#include <jau/cpp_pragma.hpp>
38
39/**
40 * @anchor jau_cfmt_header
41 * ## `snprintf` argument type checker `jau::cfmt`
42 *
43 * ### Features
44 * - jau::cfmt::check() provides strict type matching of arguments against the format string.
45 * - Have jau::cfmt::check() to be passed _before_ using std::snprintf(),
46 * removing safety concerns of the latter and benefit from its formatting and performance.
47 * - Follows [C++ Reference](https://en.cppreference.com/w/cpp/io/c/fprintf)
48 *
49 * ### Type Conversion
50 * Implementation follows type conversion rules as described
51 * in [Variadic Default Conversion](https://en.cppreference.com/w/cpp/language/variadic_arguments#Default_conversions)
52 * - float to double promotion
53 * - bool, char, short, and unscoped enumerations are converted to int or wider integer types
54 * as well as in [va_arg](https://en.cppreference.com/w/cpp/utility/variadic/va_arg)
55 * - ignore signed/unsigned type differences for integral types
56 * - void pointer tolerance
57 *
58 * ### Implementation Details
59 * - Validates arguments against format string at compile time or runtime,
60 * depending whether passed arguments are of constexpr nature (compile time).
61 * - Written in C++20 using template argument pack w/ save argument type checks
62 * - Written as constexpr, capable to be utilized at compile-time.
63 *
64 * #### Supported Conversion Specifiers and Data Types
65 *
66 * The following conversion specifiers are supported:
67 * - `c`, `s`, `d`, `o`, `x`, `X`, `u`, `f`, `e`, `E`, `a`, `A`, `g`, `G`, `p`
68 * - Their synonyms
69 * - `i` -> `d`
70 * - `F` -> `f`
71 * - Flags `-`, `+`, ` `, `0` and `#`
72 * - Asterisk `*` for field width and precision
73 *
74 * The following length modifiers are supported where allowed
75 * - `hh` [unsigned] char
76 * - `h` [unsigned] short
77 * - `l` [unsigned] long
78 * - `ll` [unsigned] long long
79 * - 'j' uintmax_t or intmax_t
80 * - 'z' size_t or ssize_t
81 * - 't' ptrdiff_t
82 * - 'L' long double
83 *
84 * See [C++ Reference](https://en.cppreference.com/w/cpp/io/c/fprintf) for details.
85 *
86 * ### Further Documentation
87 * - [C++ Reference](https://en.cppreference.com/w/cpp/io/c/fprintf)
88 * - [Linux snprintf(3) man page](https://www.man7.org/linux/man-pages/man3/snprintf.3p.html)
89 * - [FreeBSD snprintf(3) man page](https://man.freebsd.org/cgi/man.cgi?snprintf(3))
90 */
91namespace jau::cfmt {
92
93 /** \addtogroup StringUtils
94 *
95 * @{
96 */
97
105 constexpr static const char *to_string(pstate_t s) noexcept {
106 switch( s ) {
107 case pstate_t::outside: return "outside";
108 case pstate_t::start: return "start";
109 case pstate_t::field_width: return "width";
110 case pstate_t::precision: return "precision";
111 default: return "error";
112 }
113 }
125 constexpr static const char *to_string(plength_t s) noexcept {
126 switch( s ) {
127 case plength_t::hh: return "hh";
128 case plength_t::h: return "h";
129 case plength_t::none: return "";
130 case plength_t::l: return "l";
131 case plength_t::ll: return "ll";
132 case plength_t::j: return "j";
133 case plength_t::z: return "z";
134 case plength_t::t: return "t";
135 case plength_t::L: return "L";
136 default: return "n/a";
137 }
138 }
139
140 static constexpr bool isDigit(const char c) noexcept { return '0' <= c && c <= '9'; }
141
142 namespace impl {
143 class Parser; // fwd
144 }
145 struct PResult {
146 std::string_view fmt;
147 size_t pos;
148 ssize_t arg_count;
149 int line;
153
154 constexpr PResult(const std::string_view fmt_) noexcept
156
157 constexpr PResult(const PResult &pre) noexcept = default;
158
159 constexpr PResult &operator=(const PResult &x) noexcept = default;
160
161 constexpr bool hasNext() const noexcept {
162 return !error() && pos < fmt.length() - 1;
163 }
164
165 constexpr ssize_t argCount() const noexcept { return arg_count; }
166 constexpr bool error() const noexcept { return pstate_t::error == state; }
167
168 std::string toString() const {
169 const char c = pos < fmt.length() ? fmt[pos] : '@';
170 std::string s = "args ";
171 s.append(std::to_string(arg_count))
172 .append(", state ")
173 .append(to_string(state))
174 .append(", line ")
175 .append(std::to_string(line))
176 .append(", pos ")
177 .append(std::to_string(pos))
178 .append(", char `")
179 .append(std::string(1, c))
180 .append("`, length `")
181 .append(to_string(length_mod))
182 .append("`, precision ")
183 .append(std::to_string(precision_set))
184 .append(", fmt `")
185 .append(fmt)
186 .append("`");
187 return s;
188 }
189
190 private:
191 friend class impl::Parser;
192
193 constexpr bool nextSymbol(char &c) noexcept {
194 if( pos < fmt.length() - 1 ) {
195 c = fmt[++pos];
196 return true;
197 } else {
198 return false;
199 }
200 }
201 constexpr bool toConversion() noexcept {
202 if( pstate_t::outside != state ) {
203 return true; // inside conversion specifier
204 } else if( fmt[pos] == '%' ) {
205 state = pstate_t::start; // just at start of conversion specifier
206 return true;
207 } else if( pos < fmt.length() - 1 ) {
208 // seek next conversion specifier
209 const size_t q = fmt.find('%', pos + 1);
210 if( q == std::string::npos ) {
211 // no conversion specifier found
212 pos = fmt.length();
213 return false;
214 } else {
215 // new conversion specifier found
216 pos = q;
218 return true;
219 }
220 } else {
221 // end of format
222 return false;
223 }
224 }
225
226 constexpr void setError(int l) noexcept {
227 line = l;
229 if( 0 == arg_count ) {
230 arg_count = std::numeric_limits<ssize_t>::min();
231 } else if( 0 < arg_count ) {
232 arg_count *= -1;
233 }
234 }
235
236 constexpr void resetArgMods() noexcept {
238 precision_set = false;
239 }
240 };
241
242 inline std::ostream &operator<<(std::ostream &out, const PResult &pc) {
243 out << pc.toString();
244 return out;
245 }
246
247 namespace impl {
248
249 static constexpr bool verbose_error = true;
250
251 enum class no_type_t {};
252
253 class Parser {
254 public:
255 constexpr Parser() noexcept = default;
256
257 /**
258 * Parse the given argument against the current conversion specifier of the format string.
259 *
260 * Multiple rounds of parsing calls might be required, each passing the next argument or null.
261 *
262 * Parsing is completed when method returns false, either signaling an error() or completion.
263 *
264 * @tparam T The type of the given argument
265 * @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.
266 */
267 template <typename T>
268 constexpr bool parseOne(PResult &pc) const noexcept {
269 if( !pc.hasNext() ) {
270 return false; // done or error
271 }
272
273 if( !pc.toConversion() ) {
274 return false; // done
275 }
276 // pstate_t::outside != _state
277
278 char c;
279 /* skip '%' or previous `*` */
280 if( !pc.nextSymbol(c) ) {
281 pc.setError(__LINE__);
282 return false; // error
283 }
284
285 if( pstate_t::start == pc.state ) {
286 pc.state = pstate_t::field_width;
287 parseFlags(pc, c);
288
289 /* parse field width */
290 if( !parseFieldWidth<T>(pc, c) ) {
291 return !pc.error(); // error or continue with next argument for same conversion -> field_width
292 }
293 }
294
295 if( pstate_t::field_width == pc.state ) {
296 /* parse precision */
297 pc.state = pstate_t::precision;
298 if( c == '.' ) {
299 if( !parsePrecision<T>(pc, c) ) {
300 return !pc.error(); // error or continue with next argument for same conversion -> precision
301 }
302 }
303 }
304 if( !parseLengthMods(pc, c) ) {
305 return false; // error
306 }
307
308 if( !parseFmtSpec<T>(pc, c) ) {
309 return false; // error
310 }
311
312 // next conversion specifier
313 pc.state = pstate_t::outside;
314 if( !pc.nextSymbol(c) ) {
315 return false; // done
316 }
317 pc.resetArgMods();
318 return pc.hasNext();
319 }
320
321 private:
322 constexpr void parseFlags(PResult &pc, char &c) const noexcept {
323 do {
324 switch( c ) {
325 case '0': break;
326 case '-': break;
327 case '+': break;
328 case ' ': break;
329 case '#': break;
330 case '\'': break;
331 default: return; // done, not a flag
332 }
333 } while( pc.nextSymbol(c) );
334 }
335
336 /* parse field width, returns true if parsing can continue or false if next argument is required or error */
337 template <typename T>
338 constexpr bool parseFieldWidth(PResult &pc, char &c) const noexcept {
339 if( c == '*' ) {
340 if( !pc.nextSymbol(c) ) { return false; }
341
342 using U = std::remove_cv_t<T>;
343
344 if constexpr( std::is_same_v<no_type_t, T> ) {
345 pc.setError(__LINE__);
346 return false; // error
347 }
348 ++pc.arg_count;
349 if constexpr( !std::is_same_v<int, U> ) {
350 pc.setError(__LINE__);
351 return false; // error
352 }
353 return false; // next argument is required
354 } else if( isDigit(c) ) {
355 if( !pc.nextSymbol(c) ) { return false; }
356 while( isDigit(c) ) {
357 if( !pc.nextSymbol(c) ) { return false; }
358 }
359 }
360 return true; // continue with current argument
361 }
362
363 /* parse precision, returns true if parsing can continue or false if next argument is required or error */
364 template <typename T>
365 constexpr bool parsePrecision(PResult &pc, char &c) const noexcept {
366 pc.precision_set = true;
367 if( !pc.nextSymbol(c) ) { return false; }
368 if( c == '*' ) {
369 if( !pc.nextSymbol(c) ) { return false; }
370
371 using U = std::remove_cv_t<T>;
372
373 if constexpr( std::is_same_v<no_type_t, T> ) {
374 pc.setError(__LINE__);
375 return false; // error
376 }
377 ++pc.arg_count;
378 if constexpr( !std::is_same_v<int, U> ) {
379 pc.setError(__LINE__);
380 return false; // error
381 }
382 return false; // next argument is required
383 } else if( isDigit(c) ) {
384 if( !pc.nextSymbol(c) ) { return false; }
385 while( isDigit(c) ) {
386 if( !pc.nextSymbol(c) ) { return false; }
387 }
388 }
389 return true; // continue with current argument
390 }
391
392 /* parse length modifier, returns true if parsing can continue or false on error. */
393 constexpr bool parseLengthMods(PResult &pc, char &c) const noexcept {
394 if( 'h' == c ) {
395 if( !pc.nextSymbol(c) ) { return false; }
396 if( 'h' == c ) {
397 if( !pc.nextSymbol(c) ) { return false; }
398 pc.length_mod = plength_t::hh;
399 } else {
400 pc.length_mod = plength_t::h;
401 }
402 } else if( 'l' == c ) {
403 if( !pc.nextSymbol(c) ) { return false; }
404 if( 'l' == c ) {
405 if( !pc.nextSymbol(c) ) { return false; }
406 pc.length_mod = plength_t::ll;
407 } else {
408 pc.length_mod = plength_t::l;
409 }
410 } else if( 'j' == c ) {
411 if( !pc.nextSymbol(c) ) { return false; }
412 pc.length_mod = plength_t::j;
413 } else if( 'z' == c ) {
414 if( !pc.nextSymbol(c) ) { return false; }
415 pc.length_mod = plength_t::z;
416 } else if( 't' == c ) {
417 if( !pc.nextSymbol(c) ) { return false; }
418 pc.length_mod = plength_t::t;
419 } else if( 'L' == c ) {
420 if( !pc.nextSymbol(c) ) { return false; }
421 pc.length_mod = plength_t::L;
422 } else {
423 pc.length_mod = plength_t::none;
424 }
425 return true;
426 }
427
428 template <typename T>
429 constexpr bool parseFmtSpec(PResult &pc, char fmt_literal) const noexcept {
430 fmt_literal = unaliasFmtSpec(fmt_literal);
431
432 switch( fmt_literal ) {
433 case '%':
434 case 'c':
435 case 's':
436 return parseStringFmtSpec<T>(pc, fmt_literal);
437 case 'p':
438 return parseAPointerFmtSpec<T>(pc);
439 case 'd':
440 return parseSignedFmtSpec<T>(pc);
441 case 'o':
442 case 'x':
443 case 'X':
444 case 'u':
445 return parseUnsignedFmtSpec<T>(pc, fmt_literal);
446 case 'f':
447 case 'e':
448 case 'E':
449 case 'a':
450 case 'A':
451 case 'g':
452 case 'G':
453 return parseFloatFmtSpec<T>(pc, fmt_literal);
454 default:
455 pc.setError(__LINE__);
456 return false;
457 } // switch( fmt_literal )
458 }
459
460 constexpr char unaliasFmtSpec(const char fmt_literal) const noexcept {
461 switch( fmt_literal ) {
462 case 'i': return 'd';
463 case 'F': return 'f';
464 default: return fmt_literal;
465 }
466 }
467
468 template <typename T>
469 constexpr bool parseStringFmtSpec(PResult &pc, const char fmt_literal) const noexcept {
470 if constexpr( std::is_same_v<no_type_t, T> ) {
471 pc.setError(__LINE__);
472 return false;
473 }
474 switch( fmt_literal ) {
475 case '%':
476 break;
477 case 'c': {
478 ++pc.arg_count;
479 using U = std::remove_cv_t<T>;
480 switch( pc.length_mod ) {
481 case plength_t::none:
482 if constexpr( !std::is_same_v<char, U> ||
483 !std::is_same_v<int, U> ) {
484 pc.setError(__LINE__);
485 return false;
486 }
487 break;
488 case plength_t::l:
489 if constexpr( !std::is_same_v<wchar_t, U> ||
490 !std::is_same_v<wint_t, U> ) {
491 pc.setError(__LINE__);
492 return false;
493 }
494 break;
495 default:
496 pc.setError(__LINE__);
497 return false;
498 }
499 break;
500 }
501 case 's':
502 ++pc.arg_count;
503 switch( pc.length_mod ) {
504 case plength_t::none:
505 if constexpr( !std::is_pointer_v<T> ||
506 !std::is_same_v<char, std::remove_cv_t<std::remove_pointer_t<T>>> ) {
507 pc.setError(__LINE__);
508 return false;
509 }
510 break;
511 case plength_t::l:
512 if constexpr( !std::is_pointer_v<T> ||
513 !std::is_same_v<wchar_t, std::remove_cv_t<std::remove_pointer_t<T>>> ) {
514 pc.setError(__LINE__);
515 return false;
516 }
517 break;
518 default:
519 // setError();
520 pc.setError(__LINE__);
521 return false;
522 }
523 break;
524 default:
525 pc.setError(__LINE__);
526 return false;
527 }
528 return true;
529 }
530
531 template <typename T>
532 constexpr bool parseAPointerFmtSpec(PResult &pc) const noexcept {
533 pc.length_mod = plength_t::none;
534 if constexpr( std::is_same_v<no_type_t, T> ) {
535 pc.setError(__LINE__);
536 return false;
537 }
538 ++pc.arg_count;
539 if constexpr( !std::is_pointer_v<T> ) {
540 pc.setError(__LINE__);
541 return false;
542 }
543 return true;
544 }
545
546 template <typename S>
547 constexpr bool parseSignedFmtSpec(PResult &pc) const noexcept {
548 if constexpr( std::is_same_v<no_type_t, S> ) {
549 pc.setError(__LINE__);
550 return false;
551 }
552 ++pc.arg_count;
553
554 using T = std::remove_cv_t<S>;
555 // 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
556 using U = typename std::conditional_t<std::is_integral_v<T> && std::is_unsigned_v<T>, std::make_signed<T>, std::type_identity<T>>::type; // NOLINT
557
558 switch( pc.length_mod ) {
559 case plength_t::hh:
560 if constexpr( !std::is_same_v<char, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(char)) ) {
561 pc.setError(__LINE__);
562 return false;
563 }
564 break;
565 case plength_t::h:
566 if constexpr( !std::is_same_v<short, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(short)) ) {
567 pc.setError(__LINE__);
568 return false;
569 }
570 break;
571 case plength_t::none:
572 if constexpr( !std::is_same_v<int, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(int)) ) {
573 pc.setError(__LINE__);
574 return false;
575 }
576 break;
577 case plength_t::l:
578 if constexpr( !std::is_same_v<long, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(long)) ) {
579 pc.setError(__LINE__);
580 return false;
581 }
582 break;
583 case plength_t::ll:
584 if constexpr( !std::is_same_v<long long, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(long long)) ) {
585 pc.setError(__LINE__);
586 return false;
587 }
588 break;
589 case plength_t::j:
590 if constexpr( !std::is_same_v<intmax_t, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(intmax_t)) ) {
591 pc.setError(__LINE__);
592 return false;
593 }
594 break;
595 case plength_t::z:
596 if constexpr( !std::is_same_v<ssize_t, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(ssize_t)) ) {
597 pc.setError(__LINE__);
598 return false;
599 }
600 break;
601 case plength_t::t:
602 if constexpr( !std::is_same_v<ptrdiff_t, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(ptrdiff_t)) ) {
603 pc.setError(__LINE__);
604 return false;
605 }
606 break;
607 default:
608 pc.setError(__LINE__);
609 return false;
610 }
611 return true;
612 }
613
614 template <typename S>
615 constexpr bool parseUnsignedFmtSpec(PResult &pc, const char /*fmt_literal*/) const noexcept {
616 if constexpr( std::is_same_v<no_type_t, S> ) {
617 pc.setError(__LINE__);
618 return false;
619 }
620 ++pc.arg_count;
621
622 using T = std::remove_cv_t<S>;
623 // 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
624 using U = typename std::conditional_t<std::is_integral_v<T> && std::is_signed_v<T>, std::make_unsigned<T>, std::type_identity<T>>::type; // NOLINT
625
626 switch( pc.length_mod ) {
627 case plength_t::hh:
628 if constexpr( !std::is_same_v<unsigned char, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(unsigned char)) ) {
629 pc.setError(__LINE__);
630 return false;
631 }
632 break;
633 case plength_t::h:
634 if constexpr( !std::is_same_v<unsigned short, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(unsigned short)) ) {
635 pc.setError(__LINE__);
636 return false;
637 }
638 break;
639 case plength_t::none:
640 if constexpr( !std::is_same_v<unsigned int, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(unsigned int)) ) {
641 pc.setError(__LINE__);
642 return false;
643 }
644 break;
645 case plength_t::l:
646 if constexpr( !std::is_same_v<unsigned long, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(unsigned long)) ) {
647 pc.setError(__LINE__);
648 return false;
649 }
650 break;
651 case plength_t::ll:
652 if constexpr( !std::is_same_v<unsigned long long, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(unsigned long long)) ) {
653 pc.setError(__LINE__);
654 return false;
655 }
656 break;
657 case plength_t::j:
658 if constexpr( !std::is_same_v<uintmax_t, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(uintmax_t)) ) {
659 pc.setError(__LINE__);
660 return false;
661 }
662 break;
663 case plength_t::z:
664 if constexpr( !std::is_same_v<size_t, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(size_t)) ) {
665 pc.setError(__LINE__);
666 return false;
667 }
668 break;
669#if 0
670 case plength_t::t:
671 if constexpr( !std::is_same_v<unsigned ptrdiff_t, U> ) {
672 setError();
673 return false;
674 }
675 break;
676#endif
677 default:
678 pc.setError(__LINE__);
679 return false;
680 }
681 return true;
682 }
683
684 template <typename T>
685 constexpr bool parseFloatFmtSpec(PResult &pc, const char /*fmt_literal*/) const noexcept {
686 if constexpr( std::is_same_v<no_type_t, T> ) {
687 pc.setError(__LINE__);
688 return false;
689 }
690 ++pc.arg_count;
691
692 using U = std::remove_cv_t<T>;
693
694 switch( pc.length_mod ) {
695 case plength_t::none:
696 case plength_t::l:
697 if constexpr( !std::is_same_v<float, U> &&
698 !std::is_same_v<double, U> ) {
699 pc.setError(__LINE__);
700 return false;
701 }
702 break;
703 case plength_t::L:
704 if constexpr( !std::is_same_v<float, U> &&
705 !std::is_same_v<double, U> &&
706 !std::is_same_v<long double, U> ) {
707 } else {
708 pc.setError(__LINE__);
709 return false;
710 }
711 break;
712 default:
713 pc.setError(__LINE__);
714 return false;
715 }
716 return true;
717 }
718 };
719
720 } // namespace impl
721
722 /**
723 * Strict type validation of arguments against the format string.
724 *
725 * See @ref jau_cfmt_header for details
726 *
727 * @tparam Targs the argument template type pack to be validated against the format string
728 * @param fmt the snprintf format string
729 * @param args passed arguments, used for template type deduction only
730 * @return true if successfully parsed format and arguments, false otherwise.
731 * @see @ref jau_cfmt_header
732 */
733 template <typename... Targs>
734 constexpr bool check(const std::string_view fmt, const Targs &...) noexcept {
735 PResult ctx(fmt);
736 constexpr const impl::Parser p;
737 if constexpr( 0 < sizeof...(Targs) ) {
738 ((p.template parseOne<Targs>(ctx)), ...);
739 }
740 p.template parseOne<impl::no_type_t>(ctx);
741 return !ctx.error();
742 }
743 /**
744 * Strict type validation of arguments against the format string.
745 *
746 * See @ref jau_cfmt_header for details
747 *
748 * @tparam Targs the argument template type pack to be validated against the format string
749 * @param fmt the snprintf format string
750 * @return true if successfully parsed format and arguments, false otherwise.
751 * @see @ref jau_cfmt_header
752 */
753 template <typename... Targs>
754 constexpr bool check2(const std::string_view fmt) noexcept {
755 PResult ctx(fmt);
756 constexpr const impl::Parser p;
757 if constexpr( 0 < sizeof...(Targs) ) {
758 ((p.template parseOne<Targs>(ctx)), ...);
759 }
760 p.template parseOne<impl::no_type_t>(ctx);
761 return !ctx.error();
762 }
763
764 template <typename StrView, typename... Targs>
765 constexpr bool check3(StrView fmt) noexcept {
766 PResult ctx(fmt);
767 constexpr const impl::Parser p;
768 if constexpr( 0 < sizeof...(Targs) ) {
769 ((p.template parseOne<Targs>(ctx)), ...);
770 }
771 p.template parseOne<impl::no_type_t>(ctx);
772 return !ctx.error();
773 }
774
775 /**
776 * Strict type validation of arguments against the format string.
777 *
778 * See @ref jau_cfmt_header for details
779 *
780 * @tparam Targs the argument template type pack to be validated against the format string
781 * @param fmt the snprintf format string
782 * @param args passed arguments, used for template type deduction only
783 * @return PContext result object for further inspection.
784 * @see @ref jau_cfmt_header
785 */
786 template <typename... Targs>
787 constexpr PResult checkR(const std::string_view fmt, const Targs &...) noexcept {
788 PResult ctx(fmt);
789 constexpr const impl::Parser p;
790 if constexpr( 0 < sizeof...(Targs) ) {
791 ((p.template parseOne<Targs>(ctx)), ...);
792 }
793 p.template parseOne<impl::no_type_t>(ctx);
794 return ctx;
795 }
796
797 /**
798 * Strict type validation of arguments against the format string.
799 *
800 * See @ref jau_cfmt_header for details
801 *
802 * @tparam Targs the argument template type pack to be validated against the format string
803 * @param fmt the snprintf format string
804 * @return PContext result object for further inspection.
805 * @see @ref jau_cfmt_header
806 */
807 template <typename... Targs>
808 constexpr PResult checkR2(const std::string_view fmt) noexcept {
809 PResult ctx(fmt);
810 constexpr const impl::Parser p;
811 if constexpr( 0 < sizeof...(Targs) ) {
812 ((p.template parseOne<Targs>(ctx)), ...);
813 }
814 p.template parseOne<impl::no_type_t>(ctx);
815 return ctx;
816 }
817
818 /**@}*/
819
820} // namespace jau::cfmt
821
822#endif // JAU_STRING_CFMT_HPP_
constexpr Parser() noexcept=default
constexpr bool parseOne(PResult &pc) const noexcept
Parse the given argument against the current conversion specifier of the format string.
std::ostream & operator<<(std::ostream &out, const PResult &pc)
constexpr PResult checkR2(const std::string_view fmt) noexcept
Strict type validation of arguments against the format string.
static constexpr const char * to_string(pstate_t s) noexcept
constexpr bool check2(const std::string_view fmt) noexcept
Strict type validation of arguments against the format string.
constexpr PResult checkR(const std::string_view fmt, const Targs &...) noexcept
Strict type validation of arguments against the format string.
static constexpr bool isDigit(const char c) noexcept
constexpr bool check3(StrView fmt) noexcept
static constexpr bool verbose_error
Author: Sven Gothel sgothel@jausoft.com Copyright (c) 2021 Gothel Software e.K.
constexpr bool hasNext() const noexcept
constexpr bool error() const noexcept
constexpr ssize_t argCount() const noexcept
std::string_view fmt
std::string toString() const
constexpr PResult(const PResult &pre) noexcept=default
constexpr PResult & operator=(const PResult &x) noexcept=default
constexpr PResult(const std::string_view fmt_) noexcept
constexpr jau::cfmt2::PResult check(const std::string_view fmt, const Targs &...) noexcept