jaulib v1.4.0-2-g788cf73
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(std::string_view fmt_) noexcept
156
157 constexpr PResult(const PResult &pre) noexcept = default;
158 constexpr PResult &operator=(const PResult &x) noexcept = default;
159
160 constexpr bool hasNext() const noexcept {
161 return !error() && pos < fmt.length() - 1;
162 }
163
164 constexpr ssize_t argCount() const noexcept { return arg_count; }
165 constexpr bool error() const noexcept { return pstate_t::error == state; }
166
167 std::string toString() const {
168 const char c = pos < fmt.length() ? fmt[pos] : '@';
169 std::string s = "args ";
170 s.append(std::to_string(arg_count))
171 .append(", state ")
172 .append(to_string(state))
173 .append(", line ")
174 .append(std::to_string(line))
175 .append(", pos ")
176 .append(std::to_string(pos))
177 .append(", char `")
178 .append(std::string(1, c))
179 .append("`, length `")
180 .append(to_string(length_mod))
181 .append("`, precision ")
182 .append(std::to_string(precision_set))
183 .append(", fmt `")
184 .append(fmt)
185 .append("`");
186 return s;
187 }
188
189 private:
190 friend class impl::Parser;
191
192 constexpr bool nextSymbol(char &c) noexcept {
193 if( pos < fmt.length() - 1 ) {
194 c = fmt[++pos];
195 return true;
196 } else {
197 return false;
198 }
199 }
200 constexpr bool toConversion() noexcept {
201 if( pstate_t::outside != state ) {
202 return true; // inside conversion specifier
203 } else if( fmt[pos] == '%' ) {
204 state = pstate_t::start; // just at start of conversion specifier
205 return true;
206 } else if( pos < fmt.length() - 1 ) {
207 // seek next conversion specifier
208 const size_t q = fmt.find('%', pos + 1);
209 if( q == std::string::npos ) {
210 // no conversion specifier found
211 pos = fmt.length();
212 return false;
213 } else {
214 // new conversion specifier found
215 pos = q;
217 return true;
218 }
219 } else {
220 // end of format
221 return false;
222 }
223 }
224
225 constexpr void setError(int l) noexcept {
226 line = l;
228 if( 0 == arg_count ) {
229 arg_count = std::numeric_limits<ssize_t>::min();
230 } else if( 0 < arg_count ) {
231 arg_count *= -1;
232 }
233 }
234
235 constexpr void resetArgMods() noexcept {
237 precision_set = false;
238 }
239 };
240
241 inline std::ostream &operator<<(std::ostream &out, const PResult &pc) {
242 out << pc.toString();
243 return out;
244 }
245
246 namespace impl {
247
248 static constexpr bool verbose_error = true;
249
250 enum class no_type_t {};
251
252 class Parser {
253 public:
254 constexpr Parser() noexcept = default;
255
256 /**
257 * Parse the given argument against the current conversion specifier of the format string.
258 *
259 * Multiple rounds of parsing calls might be required, each passing the next argument or null.
260 *
261 * Parsing is completed when method returns false, either signaling an error() or completion.
262 *
263 * @tparam T The type of the given argument
264 * @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.
265 */
266 template <typename T>
267 constexpr bool parseOne(PResult &pc) const noexcept {
268 if( !pc.hasNext() ) {
269 return false; // done or error
270 }
271
272 if( !pc.toConversion() ) {
273 return false; // done
274 }
275 // pstate_t::outside != _state
276
277 char c;
278 /* skip '%' or previous `*` */
279 if( !pc.nextSymbol(c) ) {
280 pc.setError(__LINE__);
281 return false; // error
282 }
283
284 if( pstate_t::start == pc.state ) {
285 pc.state = pstate_t::field_width;
286 parseFlags(pc, c);
287
288 /* parse field width */
289 if( !parseFieldWidth<T>(pc, c) ) {
290 return !pc.error(); // error or continue with next argument for same conversion -> field_width
291 }
292 }
293
294 if( pstate_t::field_width == pc.state ) {
295 /* parse precision */
296 pc.state = pstate_t::precision;
297 if( c == '.' ) {
298 if( !parsePrecision<T>(pc, c) ) {
299 return !pc.error(); // error or continue with next argument for same conversion -> precision
300 }
301 }
302 }
303 if( !parseLengthMods(pc, c) ) {
304 return false; // error
305 }
306
307 if( !parseFmtSpec<T>(pc, c) ) {
308 return false; // error
309 }
310
311 // next conversion specifier
312 pc.state = pstate_t::outside;
313 if( !pc.nextSymbol(c) ) {
314 return false; // done
315 }
316 pc.resetArgMods();
317 return pc.hasNext();
318 }
319
320 private:
321 constexpr void parseFlags(PResult &pc, char &c) const noexcept {
322 do {
323 switch( c ) {
324 case '0': break;
325 case '-': break;
326 case '+': break;
327 case ' ': break;
328 case '#': break;
329 case '\'': break;
330 default: return; // done, not a flag
331 }
332 } while( pc.nextSymbol(c) );
333 }
334
335 /* parse field width, returns true if parsing can continue or false if next argument is required or error */
336 template <typename T>
337 constexpr bool parseFieldWidth(PResult &pc, char &c) const noexcept {
338 if( c == '*' ) {
339 if( !pc.nextSymbol(c) ) { return false; }
340
341 using U = std::remove_cv_t<T>;
342
343 if constexpr( std::is_same_v<no_type_t, T> ) {
344 pc.setError(__LINE__);
345 return false; // error
346 }
347 ++pc.arg_count;
348 if constexpr( !std::is_same_v<int, U> ) {
349 pc.setError(__LINE__);
350 return false; // error
351 }
352 return false; // next argument is required
353 } else if( isDigit(c) ) {
354 if( !pc.nextSymbol(c) ) { return false; }
355 while( isDigit(c) ) {
356 if( !pc.nextSymbol(c) ) { return false; }
357 }
358 }
359 return true; // continue with current argument
360 }
361
362 /* parse precision, returns true if parsing can continue or false if next argument is required or error */
363 template <typename T>
364 constexpr bool parsePrecision(PResult &pc, char &c) const noexcept {
365 pc.precision_set = true;
366 if( !pc.nextSymbol(c) ) { return false; }
367 if( c == '*' ) {
368 if( !pc.nextSymbol(c) ) { return false; }
369
370 using U = std::remove_cv_t<T>;
371
372 if constexpr( std::is_same_v<no_type_t, T> ) {
373 pc.setError(__LINE__);
374 return false; // error
375 }
376 ++pc.arg_count;
377 if constexpr( !std::is_same_v<int, U> ) {
378 pc.setError(__LINE__);
379 return false; // error
380 }
381 return false; // next argument is required
382 } else if( isDigit(c) ) {
383 if( !pc.nextSymbol(c) ) { return false; }
384 while( isDigit(c) ) {
385 if( !pc.nextSymbol(c) ) { return false; }
386 }
387 }
388 return true; // continue with current argument
389 }
390
391 /* parse length modifier, returns true if parsing can continue or false on error. */
392 constexpr bool parseLengthMods(PResult &pc, char &c) const noexcept {
393 if( 'h' == c ) {
394 if( !pc.nextSymbol(c) ) { return false; }
395 if( 'h' == c ) {
396 if( !pc.nextSymbol(c) ) { return false; }
397 pc.length_mod = plength_t::hh;
398 } else {
399 pc.length_mod = plength_t::h;
400 }
401 } else if( 'l' == c ) {
402 if( !pc.nextSymbol(c) ) { return false; }
403 if( 'l' == c ) {
404 if( !pc.nextSymbol(c) ) { return false; }
405 pc.length_mod = plength_t::ll;
406 } else {
407 pc.length_mod = plength_t::l;
408 }
409 } else if( 'j' == c ) {
410 if( !pc.nextSymbol(c) ) { return false; }
411 pc.length_mod = plength_t::j;
412 } else if( 'z' == c ) {
413 if( !pc.nextSymbol(c) ) { return false; }
414 pc.length_mod = plength_t::z;
415 } else if( 't' == c ) {
416 if( !pc.nextSymbol(c) ) { return false; }
417 pc.length_mod = plength_t::t;
418 } else if( 'L' == c ) {
419 if( !pc.nextSymbol(c) ) { return false; }
420 pc.length_mod = plength_t::L;
421 } else {
422 pc.length_mod = plength_t::none;
423 }
424 return true;
425 }
426
427 template <typename T>
428 constexpr bool parseFmtSpec(PResult &pc, char fmt_literal) const noexcept {
429 fmt_literal = unaliasFmtSpec(fmt_literal);
430
431 switch( fmt_literal ) {
432 case '%':
433 case 'c':
434 case 's':
435 return parseStringFmtSpec<T>(pc, fmt_literal);
436 case 'p':
437 return parseAPointerFmtSpec<T>(pc);
438 case 'd':
439 return parseSignedFmtSpec<T>(pc);
440 case 'o':
441 case 'x':
442 case 'X':
443 case 'u':
444 return parseUnsignedFmtSpec<T>(pc, fmt_literal);
445 case 'f':
446 case 'e':
447 case 'E':
448 case 'a':
449 case 'A':
450 case 'g':
451 case 'G':
452 return parseFloatFmtSpec<T>(pc, fmt_literal);
453 default:
454 pc.setError(__LINE__);
455 return false;
456 } // switch( fmt_literal )
457 }
458
459 constexpr char unaliasFmtSpec(const char fmt_literal) const noexcept {
460 switch( fmt_literal ) {
461 case 'i': return 'd';
462 case 'F': return 'f';
463 default: return fmt_literal;
464 }
465 }
466
467 template <typename T>
468 constexpr bool parseStringFmtSpec(PResult &pc, const char fmt_literal) const noexcept {
469 if constexpr( std::is_same_v<no_type_t, T> ) {
470 pc.setError(__LINE__);
471 return false;
472 }
473 switch( fmt_literal ) {
474 case '%':
475 break;
476 case 'c': {
477 ++pc.arg_count;
478 using U = std::remove_cv_t<T>;
479 switch( pc.length_mod ) {
480 case plength_t::none:
481 if constexpr( !std::is_same_v<char, U> ||
482 !std::is_same_v<int, U> ) {
483 pc.setError(__LINE__);
484 return false;
485 }
486 break;
487 case plength_t::l:
488 if constexpr( !std::is_same_v<wchar_t, U> ||
489 !std::is_same_v<wint_t, U> ) {
490 pc.setError(__LINE__);
491 return false;
492 }
493 break;
494 default:
495 pc.setError(__LINE__);
496 return false;
497 }
498 break;
499 }
500 case 's':
501 ++pc.arg_count;
502 switch( pc.length_mod ) {
503 case plength_t::none:
504 if constexpr( !std::is_pointer_v<T> ||
505 !std::is_same_v<char, std::remove_cv_t<std::remove_pointer_t<T>>> ) {
506 pc.setError(__LINE__);
507 return false;
508 }
509 break;
510 case plength_t::l:
511 if constexpr( !std::is_pointer_v<T> ||
512 !std::is_same_v<wchar_t, std::remove_cv_t<std::remove_pointer_t<T>>> ) {
513 pc.setError(__LINE__);
514 return false;
515 }
516 break;
517 default:
518 // setError();
519 pc.setError(__LINE__);
520 return false;
521 }
522 break;
523 default:
524 pc.setError(__LINE__);
525 return false;
526 }
527 return true;
528 }
529
530 template <typename T>
531 constexpr bool parseAPointerFmtSpec(PResult &pc) const noexcept {
532 pc.length_mod = plength_t::none;
533 if constexpr( std::is_same_v<no_type_t, T> ) {
534 pc.setError(__LINE__);
535 return false;
536 }
537 ++pc.arg_count;
538 if constexpr( !std::is_pointer_v<T> ) {
539 pc.setError(__LINE__);
540 return false;
541 }
542 return true;
543 }
544
545 template <typename S>
546 constexpr bool parseSignedFmtSpec(PResult &pc) const noexcept {
547 if constexpr( std::is_same_v<no_type_t, S> ) {
548 pc.setError(__LINE__);
549 return false;
550 }
551 ++pc.arg_count;
552
553 using T = std::remove_cv_t<S>;
554 // 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
555 using U = typename std::conditional_t<!std::is_same_v<bool, T> && std::is_integral_v<T> && std::is_unsigned_v<T>, std::make_signed<T>, std::type_identity<T>>::type; // NOLINT
556
557 switch( pc.length_mod ) {
558 case plength_t::hh:
559 if constexpr( !std::is_same_v<char, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(char)) ) { // NOLINT(bugprone-sizeof-expression)
560 pc.setError(__LINE__);
561 return false;
562 }
563 break;
564 case plength_t::h:
565 if constexpr( !std::is_same_v<short, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(short)) ) { // NOLINT(bugprone-sizeof-expression)
566 pc.setError(__LINE__);
567 return false;
568 }
569 break;
570 case plength_t::none:
571 if constexpr( !std::is_same_v<int, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(int)) ) { // NOLINT(bugprone-sizeof-expression)
572 pc.setError(__LINE__);
573 return false;
574 }
575 break;
576 case plength_t::l:
577 if constexpr( !std::is_same_v<long, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(long)) ) { // NOLINT(bugprone-sizeof-expression)
578 pc.setError(__LINE__);
579 return false;
580 }
581 break;
582 case plength_t::ll:
583 if constexpr( !std::is_same_v<long long, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(long long)) ) { // NOLINT(bugprone-sizeof-expression)
584 pc.setError(__LINE__);
585 return false;
586 }
587 break;
588 case plength_t::j:
589 if constexpr( !std::is_same_v<intmax_t, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(intmax_t)) ) { // NOLINT(bugprone-sizeof-expression)
590 pc.setError(__LINE__);
591 return false;
592 }
593 break;
594 case plength_t::z:
595 if constexpr( !std::is_same_v<ssize_t, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(ssize_t)) ) { // NOLINT(bugprone-sizeof-expression)
596 pc.setError(__LINE__);
597 return false;
598 }
599 break;
600 case plength_t::t:
601 if constexpr( !std::is_same_v<ptrdiff_t, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(ptrdiff_t)) ) { // NOLINT(bugprone-sizeof-expression)
602 pc.setError(__LINE__);
603 return false;
604 }
605 break;
606 default:
607 pc.setError(__LINE__);
608 return false;
609 }
610 return true;
611 }
612
613 template <typename S>
614 constexpr bool parseUnsignedFmtSpec(PResult &pc, const char /*fmt_literal*/) const noexcept {
615 if constexpr( std::is_same_v<no_type_t, S> ) {
616 pc.setError(__LINE__);
617 return false;
618 }
619 ++pc.arg_count;
620
621 using T = std::remove_cv_t<S>;
622 // 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
623 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
624
625 switch( pc.length_mod ) {
626 case plength_t::hh:
627 if constexpr( !std::is_same_v<unsigned char, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(unsigned char)) ) { // NOLINT(bugprone-sizeof-expression)
628 pc.setError(__LINE__);
629 return false;
630 }
631 break;
632 case plength_t::h:
633 if constexpr( !std::is_same_v<unsigned short, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(unsigned short)) ) { // NOLINT(bugprone-sizeof-expression)
634 pc.setError(__LINE__);
635 return false;
636 }
637 break;
638 case plength_t::none:
639 if constexpr( !std::is_same_v<unsigned int, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(unsigned int)) ) { // NOLINT(bugprone-sizeof-expression)
640 pc.setError(__LINE__);
641 return false;
642 }
643 break;
644 case plength_t::l:
645 if constexpr( !std::is_same_v<unsigned long, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(unsigned long)) ) { // NOLINT(bugprone-sizeof-expression)
646 pc.setError(__LINE__);
647 return false;
648 }
649 break;
650 case plength_t::ll:
651 if constexpr( !std::is_same_v<unsigned long long, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(unsigned long long)) ) { // NOLINT(bugprone-sizeof-expression)
652 pc.setError(__LINE__);
653 return false;
654 }
655 break;
656 case plength_t::j:
657 if constexpr( !std::is_same_v<uintmax_t, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(uintmax_t)) ) { // NOLINT(bugprone-sizeof-expression)
658 pc.setError(__LINE__);
659 return false;
660 }
661 break;
662 case plength_t::z:
663 if constexpr( !std::is_same_v<size_t, U> && (!std::is_integral_v<U> || sizeof(U) > sizeof(size_t)) ) { // NOLINT(bugprone-sizeof-expression)
664 pc.setError(__LINE__);
665 return false;
666 }
667 break;
668#if 0
669 case plength_t::t:
670 if constexpr( !std::is_same_v<unsigned ptrdiff_t, U> ) {
671 setError();
672 return false;
673 }
674 break;
675#endif
676 default:
677 pc.setError(__LINE__);
678 return false;
679 }
680 return true;
681 }
682
683 template <typename T>
684 constexpr bool parseFloatFmtSpec(PResult &pc, const char /*fmt_literal*/) const noexcept {
685 if constexpr( std::is_same_v<no_type_t, T> ) {
686 pc.setError(__LINE__);
687 return false;
688 }
689 ++pc.arg_count;
690
691 using U = std::remove_cv_t<T>;
692
693 switch( pc.length_mod ) {
694 case plength_t::none:
695 case plength_t::l:
696 if constexpr( !std::is_same_v<float, U> &&
697 !std::is_same_v<double, U> ) {
698 pc.setError(__LINE__);
699 return false;
700 }
701 break;
702 case plength_t::L:
703 if constexpr( !std::is_same_v<float, U> &&
704 !std::is_same_v<double, U> &&
705 !std::is_same_v<long double, U> ) {
706 } else {
707 pc.setError(__LINE__);
708 return false;
709 }
710 break;
711 default:
712 pc.setError(__LINE__);
713 return false;
714 }
715 return true;
716 }
717 };
718
719 } // namespace impl
720
721 /**
722 * Strict type validation of arguments against the format string.
723 *
724 * See @ref jau_cfmt_header for details
725 *
726 * @tparam Targs the argument template type pack to be validated against the format string
727 * @param fmt the snprintf format string
728 * @param args passed arguments, used for template type deduction only
729 * @return true if successfully parsed format and arguments, false otherwise.
730 * @see @ref jau_cfmt_header
731 */
732 template <typename... Targs>
733 constexpr bool check(const std::string_view fmt, const Targs &...) noexcept {
734 PResult ctx(fmt);
735 constexpr const impl::Parser p;
736 if constexpr( 0 < sizeof...(Targs) ) {
737 ((p.template parseOne<Targs>(ctx)), ...);
738 }
739 p.template parseOne<impl::no_type_t>(ctx);
740 return !ctx.error();
741 }
742 /**
743 * Strict type validation of arguments against the format string.
744 *
745 * See @ref jau_cfmt_header for details
746 *
747 * @tparam Targs the argument template type pack to be validated against the format string
748 * @param fmt the snprintf format string
749 * @return true if successfully parsed format and arguments, false otherwise.
750 * @see @ref jau_cfmt_header
751 */
752 template <typename... Targs>
753 constexpr bool check2(const std::string_view fmt) noexcept {
754 PResult ctx(fmt);
755 constexpr const impl::Parser p;
756 if constexpr( 0 < sizeof...(Targs) ) {
757 ((p.template parseOne<Targs>(ctx)), ...);
758 }
759 p.template parseOne<impl::no_type_t>(ctx);
760 return !ctx.error();
761 }
762
763 template <typename StrView, typename... Targs>
764 constexpr bool check3(StrView fmt) noexcept {
765 PResult ctx(fmt);
766 constexpr const impl::Parser p;
767 if constexpr( 0 < sizeof...(Targs) ) {
768 ((p.template parseOne<Targs>(ctx)), ...);
769 }
770 p.template parseOne<impl::no_type_t>(ctx);
771 return !ctx.error();
772 }
773
774 /**
775 * Strict type validation of arguments against the format string.
776 *
777 * See @ref jau_cfmt_header for details
778 *
779 * @tparam Targs the argument template type pack to be validated against the format string
780 * @param fmt the snprintf format string
781 * @param args passed arguments, used for template type deduction only
782 * @return PContext result object for further inspection.
783 * @see @ref jau_cfmt_header
784 */
785 template <typename... Targs>
786 constexpr PResult checkR(const std::string_view fmt, const Targs &...) noexcept {
787 PResult ctx(fmt);
788 constexpr const impl::Parser p;
789 if constexpr( 0 < sizeof...(Targs) ) {
790 ((p.template parseOne<Targs>(ctx)), ...);
791 }
792 p.template parseOne<impl::no_type_t>(ctx);
793 return ctx;
794 }
795
796 /**
797 * Strict type validation of arguments against the format string.
798 *
799 * See @ref jau_cfmt_header for details
800 *
801 * @tparam Targs the argument template type pack to be validated against the format string
802 * @param fmt the snprintf format string
803 * @return PContext result object for further inspection.
804 * @see @ref jau_cfmt_header
805 */
806 template <typename... Targs>
807 constexpr PResult checkR2(const std::string_view fmt) noexcept {
808 PResult ctx(fmt);
809 constexpr const impl::Parser p;
810 if constexpr( 0 < sizeof...(Targs) ) {
811 ((p.template parseOne<Targs>(ctx)), ...);
812 }
813 p.template parseOne<impl::no_type_t>(ctx);
814 return ctx;
815 }
816
817 /**@}*/
818
819} // namespace jau::cfmt
820
821#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(std::string_view fmt_) noexcept
constexpr PResult & operator=(const PResult &x) noexcept=default
static constexpr jau::cfmt2::PResult check(const std::string_view fmt, const Targs &...) noexcept