Skip to content

Commit fadc6b9

Browse files
author
MarcoFalke
committed
refactor: Check translatable format strings at compile-time
1 parent fa1d5ac commit fadc6b9

File tree

3 files changed

+38
-24
lines changed

3 files changed

+38
-24
lines changed

src/test/fuzz/strprintf.cpp

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ FUZZ_TARGET(str_printf)
1818
{
1919
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
2020
const std::string format_string = fuzzed_data_provider.ConsumeRandomLengthString(64);
21-
const bilingual_str bilingual_string{format_string, format_string};
2221

2322
const int digits_in_format_specifier = std::count_if(format_string.begin(), format_string.end(), IsDigit);
2423

@@ -53,27 +52,21 @@ FUZZ_TARGET(str_printf)
5352
fuzzed_data_provider,
5453
[&] {
5554
(void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32));
56-
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeRandomLengthString(32));
5755
},
5856
[&] {
5957
(void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str());
60-
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str());
6158
},
6259
[&] {
6360
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<signed char>());
64-
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<signed char>());
6561
},
6662
[&] {
6763
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<unsigned char>());
68-
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<unsigned char>());
6964
},
7065
[&] {
7166
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<char>());
72-
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<char>());
7367
},
7468
[&] {
7569
(void)strprintf(format_string, fuzzed_data_provider.ConsumeBool());
76-
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeBool());
7770
});
7871
} catch (const tinyformat::format_error&) {
7972
}
@@ -99,35 +92,27 @@ FUZZ_TARGET(str_printf)
9992
fuzzed_data_provider,
10093
[&] {
10194
(void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<float>());
102-
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeFloatingPoint<float>());
10395
},
10496
[&] {
10597
(void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint<double>());
106-
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeFloatingPoint<double>());
10798
},
10899
[&] {
109100
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int16_t>());
110-
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int16_t>());
111101
},
112102
[&] {
113103
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint16_t>());
114-
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint16_t>());
115104
},
116105
[&] {
117106
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int32_t>());
118-
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int32_t>());
119107
},
120108
[&] {
121109
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint32_t>());
122-
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint32_t>());
123110
},
124111
[&] {
125112
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<int64_t>());
126-
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<int64_t>());
127113
},
128114
[&] {
129115
(void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral<uint64_t>());
130-
(void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral<uint64_t>());
131116
});
132117
} catch (const tinyformat::format_error&) {
133118
}

src/test/translation_tests.cpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,26 @@
99

1010
BOOST_AUTO_TEST_SUITE(translation_tests)
1111

12+
static TranslateFn translate{[](const char * str) { return strprintf("t(%s)", str); }};
13+
14+
// Custom translation function _t(), similar to _() but internal to this test.
15+
consteval auto _t(util::TranslatedLiteral str)
16+
{
17+
str.translate_fn = &translate;
18+
return str;
19+
}
20+
1221
BOOST_AUTO_TEST_CASE(translation_namedparams)
1322
{
1423
bilingual_str arg{"original", "translated"};
15-
bilingual_str format{"original [%s]", "translated [%s]"};
16-
bilingual_str result{strprintf(format, arg)};
24+
bilingual_str result{strprintf(_t("original [%s]"), arg)};
1725
BOOST_CHECK_EQUAL(result.original, "original [original]");
18-
BOOST_CHECK_EQUAL(result.translated, "translated [translated]");
26+
BOOST_CHECK_EQUAL(result.translated, "t(original [translated])");
27+
28+
util::TranslatedLiteral arg2{"original", &translate};
29+
bilingual_str result2{strprintf(_t("original [%s]"), arg2)};
30+
BOOST_CHECK_EQUAL(result2.original, "original [original]");
31+
BOOST_CHECK_EQUAL(result2.translated, "t(original [t(original)])");
1932
}
2033

2134
BOOST_AUTO_TEST_SUITE_END()

src/util/translation.h

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,27 +67,43 @@ template<typename T>
6767
T operator+(const T& lhs, const TranslatedLiteral& rhs) { return lhs + static_cast<T>(rhs); }
6868
template<typename T>
6969
T operator+(const TranslatedLiteral& lhs, const T& rhs) { return static_cast<T>(lhs) + rhs; }
70+
71+
template <unsigned num_params>
72+
struct BilingualFmt {
73+
const ConstevalFormatString<num_params> original;
74+
TranslatedLiteral lit;
75+
consteval BilingualFmt(TranslatedLiteral l) : original{l.original}, lit{l} {}
76+
};
7077
} // namespace util
7178

7279
consteval auto _(util::TranslatedLiteral str) { return str; }
7380

7481
/** Mark a bilingual_str as untranslated */
7582
inline bilingual_str Untranslated(std::string original) { return {original, original}; }
7683

77-
// Provide an overload of tinyformat::format which can take bilingual_str arguments.
84+
// Provide an overload of tinyformat::format for BilingualFmt format strings and bilingual_str or TranslatedLiteral args.
7885
namespace tinyformat {
7986
template <typename... Args>
80-
bilingual_str format(const bilingual_str& fmt, const Args&... args)
87+
bilingual_str format(util::BilingualFmt<sizeof...(Args)> fmt, const Args&... args)
8188
{
82-
const auto translate_arg{[](const auto& arg, bool translated) -> const auto& {
89+
const auto original_arg{[](const auto& arg) -> const auto& {
90+
if constexpr (std::is_same_v<decltype(arg), const bilingual_str&>) {
91+
return arg.original;
92+
} else if constexpr (std::is_same_v<decltype(arg), const util::TranslatedLiteral&>) {
93+
return arg.original;
94+
} else {
95+
return arg;
96+
}
97+
}};
98+
const auto translated_arg{[](const auto& arg) -> const auto& {
8399
if constexpr (std::is_same_v<decltype(arg), const bilingual_str&>) {
84-
return translated ? arg.translated : arg.original;
100+
return arg.translated;
85101
} else {
86102
return arg;
87103
}
88104
}};
89-
return bilingual_str{tfm::format(fmt.original, translate_arg(args, false)...),
90-
tfm::format(fmt.translated, translate_arg(args, true)...)};
105+
return bilingual_str{tfm::format(fmt.original, original_arg(args)...),
106+
tfm::format(std::string{fmt.lit}, translated_arg(args)...)};
91107
}
92108
} // namespace tinyformat
93109

0 commit comments

Comments
 (0)