17
17
#include < vector>
18
18
19
19
namespace util {
20
+ namespace detail {
21
+ template <unsigned num_params>
22
+ constexpr static void CheckNumFormatSpecifiers (const char * str)
23
+ {
24
+ unsigned count_normal{0 }; // Number of "normal" specifiers, like %s
25
+ unsigned count_pos{0 }; // Max number in positional specifier, like %8$s
26
+ for (auto it{str}; *it != ' \0 ' ; ++it) {
27
+ if (*it != ' %' || *++it == ' %' ) continue ; // Skip escaped %%
28
+
29
+ auto add_arg = [&] {
30
+ unsigned maybe_num{0 };
31
+ while (' 0' <= *it && *it <= ' 9' ) {
32
+ maybe_num *= 10 ;
33
+ maybe_num += *it - ' 0' ;
34
+ ++it;
35
+ }
36
+
37
+ if (*it == ' $' ) {
38
+ ++it;
39
+ // Positional specifier, like %8$s
40
+ if (maybe_num == 0 ) throw " Positional format specifier must have position of at least 1" ;
41
+ count_pos = std::max (count_pos, maybe_num);
42
+ } else {
43
+ // Non-positional specifier, like %s
44
+ ++count_normal;
45
+ }
46
+ };
47
+
48
+ // Increase argument count and consume positional specifier, if present.
49
+ add_arg ();
50
+
51
+ // Consume flags.
52
+ while (*it == ' #' || *it == ' 0' || *it == ' -' || *it == ' ' || *it == ' +' ) ++it;
53
+
54
+ auto parse_size = [&] {
55
+ if (*it == ' *' ) {
56
+ ++it;
57
+ add_arg ();
58
+ } else {
59
+ while (' 0' <= *it && *it <= ' 9' ) ++it;
60
+ }
61
+ };
62
+
63
+ // Consume dynamic or static width value.
64
+ parse_size ();
65
+
66
+ // Consume dynamic or static precision value.
67
+ if (*it == ' .' ) {
68
+ ++it;
69
+ parse_size ();
70
+ }
71
+
72
+ if (*it == ' \0 ' ) throw " Format specifier incorrectly terminated by end of string" ;
73
+
74
+ // Length and type in "[flags][width][.precision][length]type"
75
+ // is not checked. Parsing continues with the next '%'.
76
+ }
77
+ if (count_normal && count_pos) throw " Format specifiers must be all positional or all non-positional!" ;
78
+ unsigned count{count_normal | count_pos};
79
+ if (num_params != count) throw " Format specifier count must match the argument count!" ;
80
+ }
81
+ } // namespace detail
82
+
20
83
/* *
21
84
* @brief A wrapper for a compile-time partially validated format string
22
85
*
@@ -28,66 +91,7 @@ namespace util {
28
91
template <unsigned num_params>
29
92
struct ConstevalFormatString {
30
93
const char * const fmt;
31
- consteval ConstevalFormatString (const char * str) : fmt{str} { Detail_CheckNumFormatSpecifiers (fmt); }
32
- constexpr static void Detail_CheckNumFormatSpecifiers (const char * str)
33
- {
34
- unsigned count_normal{0 }; // Number of "normal" specifiers, like %s
35
- unsigned count_pos{0 }; // Max number in positional specifier, like %8$s
36
- for (auto it{str}; *it != ' \0 ' ; ++it) {
37
- if (*it != ' %' || *++it == ' %' ) continue ; // Skip escaped %%
38
-
39
- auto add_arg = [&] {
40
- unsigned maybe_num{0 };
41
- while (' 0' <= *it && *it <= ' 9' ) {
42
- maybe_num *= 10 ;
43
- maybe_num += *it - ' 0' ;
44
- ++it;
45
- }
46
-
47
- if (*it == ' $' ) {
48
- ++it;
49
- // Positional specifier, like %8$s
50
- if (maybe_num == 0 ) throw " Positional format specifier must have position of at least 1" ;
51
- count_pos = std::max (count_pos, maybe_num);
52
- } else {
53
- // Non-positional specifier, like %s
54
- ++count_normal;
55
- }
56
- };
57
-
58
- // Increase argument count and consume positional specifier, if present.
59
- add_arg ();
60
-
61
- // Consume flags.
62
- while (*it == ' #' || *it == ' 0' || *it == ' -' || *it == ' ' || *it == ' +' ) ++it;
63
-
64
- auto parse_size = [&] {
65
- if (*it == ' *' ) {
66
- ++it;
67
- add_arg ();
68
- } else {
69
- while (' 0' <= *it && *it <= ' 9' ) ++it;
70
- }
71
- };
72
-
73
- // Consume dynamic or static width value.
74
- parse_size ();
75
-
76
- // Consume dynamic or static precision value.
77
- if (*it == ' .' ) {
78
- ++it;
79
- parse_size ();
80
- }
81
-
82
- if (*it == ' \0 ' ) throw " Format specifier incorrectly terminated by end of string" ;
83
-
84
- // Length and type in "[flags][width][.precision][length]type"
85
- // is not checked. Parsing continues with the next '%'.
86
- }
87
- if (count_normal && count_pos) throw " Format specifiers must be all positional or all non-positional!" ;
88
- unsigned count{count_normal | count_pos};
89
- if (num_params != count) throw " Format specifier count must match the argument count!" ;
90
- }
94
+ consteval ConstevalFormatString (const char * str) : fmt{str} { detail::CheckNumFormatSpecifiers<num_params>(fmt); }
91
95
};
92
96
93
97
void ReplaceAll (std::string& in_out, const std::string& search, const std::string& substitute);
0 commit comments