Skip to content

Commit fa72646

Browse files
MarcoFalkeryanofsky
andcommitted
move-only: Detail_CheckNumFormatSpecifiers and G_TRANSLATION_FUN
This is required for a future commit. Can be reviewed via the git options --color-moved=dimmed-zebra --color-moved-ws=ignore-all-space Also move util::detail::Hex to a proper namespace instead of an inline namespace so it doesn't conflict with the new util::detail namespace, and won't create other problems for callers trying to use the inline namespaces. Also fix a misleading comment in util_string_tests.cpp. Co-Authored-By: Ryan Ofsky <[email protected]>
1 parent faff840 commit fa72646

File tree

4 files changed

+88
-85
lines changed

4 files changed

+88
-85
lines changed

src/test/util_string_tests.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ BOOST_AUTO_TEST_SUITE(util_string_tests)
1616
template <unsigned NumArgs>
1717
inline void PassFmt(util::ConstevalFormatString<NumArgs> fmt)
1818
{
19-
// This was already executed at compile-time, but is executed again at run-time to avoid -Wunused.
20-
decltype(fmt)::Detail_CheckNumFormatSpecifiers(fmt.fmt);
19+
// Execute compile-time check again at run-time to get code coverage stats
20+
util::detail::CheckNumFormatSpecifiers<NumArgs>(fmt.fmt);
2121
}
2222
template <unsigned WrongNumArgs>
2323
inline void FailFmtWithError(const char* wrong_fmt, std::string_view error)
2424
{
25-
BOOST_CHECK_EXCEPTION(util::ConstevalFormatString<WrongNumArgs>::Detail_CheckNumFormatSpecifiers(wrong_fmt), const char*, HasReason(error));
25+
BOOST_CHECK_EXCEPTION(util::detail::CheckNumFormatSpecifiers<WrongNumArgs>(wrong_fmt), const char*, HasReason{error});
2626
}
2727

2828
BOOST_AUTO_TEST_CASE(ConstevalFormatString_NumSpec)

src/util/strencodings.h

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,24 @@ consteval uint8_t ConstevalHexDigit(const char c)
377377
throw "Only lowercase hex digits are allowed, for consistency";
378378
}
379379

380+
namespace detail {
381+
template <size_t N>
382+
struct Hex {
383+
std::array<std::byte, N / 2> bytes{};
384+
consteval Hex(const char (&hex_str)[N])
385+
// 2 hex digits required per byte + implicit null terminator
386+
requires(N % 2 == 1)
387+
{
388+
if (hex_str[N - 1]) throw "null terminator required";
389+
for (std::size_t i = 0; i < bytes.size(); ++i) {
390+
bytes[i] = static_cast<std::byte>(
391+
(ConstevalHexDigit(hex_str[2 * i]) << 4) |
392+
ConstevalHexDigit(hex_str[2 * i + 1]));
393+
}
394+
}
395+
};
396+
} // namespace detail
397+
380398
/**
381399
* ""_hex is a compile-time user-defined literal returning a
382400
* `std::array<std::byte>`, equivalent to ParseHex(). Variants provided:
@@ -407,25 +425,6 @@ consteval uint8_t ConstevalHexDigit(const char c)
407425
* time/runtime barrier.
408426
*/
409427
inline namespace hex_literals {
410-
namespace detail {
411-
412-
template <size_t N>
413-
struct Hex {
414-
std::array<std::byte, N / 2> bytes{};
415-
consteval Hex(const char (&hex_str)[N])
416-
// 2 hex digits required per byte + implicit null terminator
417-
requires(N % 2 == 1)
418-
{
419-
if (hex_str[N - 1]) throw "null terminator required";
420-
for (std::size_t i = 0; i < bytes.size(); ++i) {
421-
bytes[i] = static_cast<std::byte>(
422-
(ConstevalHexDigit(hex_str[2 * i]) << 4) |
423-
ConstevalHexDigit(hex_str[2 * i + 1]));
424-
}
425-
}
426-
};
427-
428-
} // namespace detail
429428

430429
template <util::detail::Hex str>
431430
constexpr auto operator""_hex() { return str.bytes; }

src/util/string.h

Lines changed: 64 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,69 @@
1717
#include <vector>
1818

1919
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+
2083
/**
2184
* @brief A wrapper for a compile-time partially validated format string
2285
*
@@ -28,66 +91,7 @@ namespace util {
2891
template <unsigned num_params>
2992
struct ConstevalFormatString {
3093
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); }
9195
};
9296

9397
void ReplaceAll(std::string& in_out, const std::string& search, const std::string& substitute);

src/util/translation.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
#include <functional>
1111
#include <string>
1212

13+
/** Translate a message to the native language of the user. */
14+
const extern std::function<std::string(const char*)> G_TRANSLATION_FUN;
15+
1316
/**
1417
* Bilingual messages:
1518
* - in GUI: user's native language + untranslated (i.e. English)
@@ -64,9 +67,6 @@ bilingual_str format(const bilingual_str& fmt, const Args&... args)
6467
}
6568
} // namespace tinyformat
6669

67-
/** Translate a message to the native language of the user. */
68-
const extern std::function<std::string(const char*)> G_TRANSLATION_FUN;
69-
7070
struct ConstevalStringLiteral {
7171
const char* const lit;
7272
consteval ConstevalStringLiteral(const char* str) : lit{str} {}

0 commit comments

Comments
 (0)