Skip to content

Commit 12f0ebd

Browse files
authored
Merge pull request #27 from acd1034/4-address-wchar_t
Address wchar_t
2 parents eda7a98 + 9bfa1a0 commit 12f0ebd

File tree

2 files changed

+76
-29
lines changed

2 files changed

+76
-29
lines changed

include/strtpl/string_template.hpp

Lines changed: 58 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,37 @@
1111

1212
namespace strtpl {
1313

14+
// TYPED_LITERAL
15+
// See https://github.com/microsoft/STL/blob/17fde2cbab6e8724d81c9555237c9a623d7fb954/tests/std/tests/P0220R1_string_view/test.cpp#L260-L277
16+
17+
template <class CharT>
18+
struct choose_literal; // not defined
19+
20+
template <>
21+
struct choose_literal<char> {
22+
static constexpr const char*
23+
choose(const char* s, const wchar_t*) {
24+
return s;
25+
}
26+
};
27+
28+
template <>
29+
struct choose_literal<wchar_t> {
30+
static constexpr const wchar_t*
31+
choose(const char*, const wchar_t* s) {
32+
return s;
33+
}
34+
};
35+
36+
#define TYPED_LITERAL(CharT, Literal) (choose_literal<CharT>::choose(Literal, L##Literal))
37+
1438
// match_results_format
1539

16-
template <class BidirectionalIter, class Allocator, class OutputIter, class ST>
40+
template <class BiIter, class Allocator, class OutputIter, class ST>
1741
OutputIter
1842
match_results_format(
19-
const std::match_results<BidirectionalIter, Allocator>& mo, OutputIter out,
20-
std::basic_string_view<typename std::iterator_traits<BidirectionalIter>::value_type, ST> fmt,
43+
const std::match_results<BiIter, Allocator>& mo, OutputIter out,
44+
std::basic_string_view<typename std::iterator_traits<BiIter>::value_type, ST> fmt,
2145
std::regex_constants::match_flag_type flags = std::regex_constants::format_default) {
2246
return mo.format(out, fmt.data(), fmt.data() + fmt.size(), flags);
2347
}
@@ -30,8 +54,9 @@ namespace strtpl {
3054
std::regex_constants::match_flag_type flags = std::regex_constants::match_default) {
3155
std::basic_string<CharT, ST> r;
3256
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
33-
const std::basic_regex<CharT> re{R"([.*+?^${}()|[\]\\])"};
34-
std::regex_replace(std::back_inserter(r), s.begin(), s.end(), re, "\\$&", flags);
57+
const std::basic_regex<CharT> re(TYPED_LITERAL(CharT, R"([.*+?^${}()|[\]\\])"));
58+
std::regex_replace(std::back_inserter(r), s.begin(), s.end(), re, TYPED_LITERAL(CharT, "\\$&"),
59+
flags);
3560
return r;
3661
}
3762

@@ -44,30 +69,29 @@ namespace strtpl {
4469
struct is_std_basic_string_view_with_char_type<std::basic_string_view<CharT, ST>, CharT>
4570
: std::true_type {};
4671

47-
template <class BidirectionalIter, class Traits, class CharT, class Fn>
48-
inline constexpr bool regex_replace_fn_constraint = std::conjunction_v<
49-
std::is_invocable<Fn&, const std::match_results<BidirectionalIter>&>,
50-
is_std_basic_string_view_with_char_type<
51-
std::invoke_result_t<Fn&, const std::match_results<BidirectionalIter>&>, CharT>>;
72+
template <class BiIter, class Traits, class CharT, class Fn>
73+
inline constexpr bool regex_replace_fn_constraint =
74+
std::conjunction_v<std::is_invocable<Fn&, const std::match_results<BiIter>&>,
75+
is_std_basic_string_view_with_char_type<
76+
std::invoke_result_t<Fn&, const std::match_results<BiIter>&>, CharT>>;
5277

5378
// clang-format off
54-
template <class OutputIter, class BidirectionalIter, class Traits, class CharT, class Fn>
55-
requires regex_replace_fn_constraint<BidirectionalIter, Traits, CharT, Fn>
79+
template <class OutputIter, class BiIter, class Traits, class CharT, class Fn>
80+
requires regex_replace_fn_constraint<BiIter, Traits, CharT, Fn>
5681
OutputIter
5782
// clang-format on
5883
regex_replace_fn(
59-
OutputIter out, BidirectionalIter first, BidirectionalIter last,
60-
const std::basic_regex<CharT, Traits>& re, Fn fn,
84+
OutputIter out, BiIter first, BiIter last, const std::basic_regex<CharT, Traits>& re, Fn fn,
6185
std::regex_constants::match_flag_type flags = std::regex_constants::match_default) {
62-
using Iter = std::regex_iterator<BidirectionalIter, CharT, Traits>;
86+
using Iter = std::regex_iterator<BiIter, CharT, Traits>;
6387
Iter i(first, last, re, flags);
6488
Iter eof;
6589
const bool format_copy = !(flags & std::regex_constants::format_no_copy);
6690
if (i == eof) {
6791
if (format_copy)
6892
out = std::copy(first, last, out);
6993
} else {
70-
std::sub_match<BidirectionalIter> lm;
94+
std::sub_match<BiIter> lm;
7195
const bool format_first_only = flags & std::regex_constants::format_first_only;
7296
for (; i != eof; ++i) {
7397
if (format_copy)
@@ -97,19 +121,18 @@ namespace strtpl {
97121

98122
// regex_count
99123

100-
template <class BidirectionalIter, class Traits, class CharT>
124+
template <class BiIter, class Traits, class CharT>
101125
std::pair<std::ptrdiff_t, std::ptrdiff_t>
102-
regex_count(BidirectionalIter first, BidirectionalIter last,
103-
const std::basic_regex<CharT, Traits>& re,
126+
regex_count(BiIter first, BiIter last, const std::basic_regex<CharT, Traits>& re,
104127
std::regex_constants::match_flag_type flags = std::regex_constants::match_default) {
105-
using Iter = std::regex_iterator<BidirectionalIter, CharT, Traits>;
128+
using Iter = std::regex_iterator<BiIter, CharT, Traits>;
106129
Iter i(first, last, re, flags);
107130
Iter eof;
108131
std::ptrdiff_t n = 0, m = 0;
109132
if (i == eof) {
110133
m = std::distance(first, last);
111134
} else {
112-
std::sub_match<BidirectionalIter> lm;
135+
std::sub_match<BiIter> lm;
113136
const bool format_first_only = flags & std::regex_constants::format_first_only;
114137
for (; i != eof; ++i) {
115138
++n;
@@ -159,12 +182,12 @@ namespace strtpl {
159182
return i == end(map) ? throw std::out_of_range("strtpl::at") : get<1>(*i);
160183
}
161184

162-
template <class BidirectionalIter>
185+
template <class BiIter>
163186
void
164-
_invalid(BidirectionalIter first, BidirectionalIter last) {
187+
_invalid(BiIter first, BiIter last) {
188+
using CharT = typename std::iterator_traits<BiIter>::value_type;
165189
// See https://docs.python.org/ja/3/library/stdtypes.html#str.splitlines
166-
const std::basic_regex<typename std::iterator_traits<BidirectionalIter>::value_type> re{
167-
R"((\r\n?|[\n\v\f]))"};
190+
const std::basic_regex<CharT> re(TYPED_LITERAL(CharT, R"((\r\n?|[\n\v\f]))"));
168191
const auto [lineno, colno] = regex_count(first, last, re);
169192
auto msg = "Invalid placeholder in string: line " + std::to_string(lineno + 1) + ", col "
170193
+ std::to_string(colno + 1);
@@ -178,7 +201,7 @@ namespace strtpl {
178201
std::basic_string_view<CharT> delimiter{};
179202
std::basic_string_view<CharT> idpattern{};
180203
std::basic_string_view<CharT> braceidpattern{};
181-
const std::basic_string_view<CharT> invalid{"()"};
204+
const std::basic_string_view<CharT> invalid{TYPED_LITERAL(CharT, "()")};
182205
std::regex_constants::match_flag_type flags = std::regex_constants::match_default;
183206

184207
string_template() = default;
@@ -202,10 +225,12 @@ namespace strtpl {
202225
const std::basic_regex<CharT> re{[this] {
203226
using namespace hidden_ops::string_view_ops;
204227
const auto delim = regex_escape(delimiter);
205-
const auto escape = "(" + delim + ")";
206-
return delim + "(?:" + idpattern + "|\\{" + braceidpattern + "\\}|" + escape + "|" + invalid
207-
+ ")";
228+
const auto escape = TYPED_LITERAL(CharT, "(") + delim + TYPED_LITERAL(CharT, ")");
229+
return delim + TYPED_LITERAL(CharT, "(?:") + idpattern + TYPED_LITERAL(CharT, "|\\{")
230+
+ braceidpattern + TYPED_LITERAL(CharT, "\\}|") + escape + TYPED_LITERAL(CharT, "|")
231+
+ invalid + TYPED_LITERAL(CharT, ")");
208232
}()};
233+
209234
const auto convert =
210235
[&delim = delimiter, first = s.begin(),
211236
&map](const std::match_results<typename std::basic_string_view<CharT, ST>::iterator>& mo)
@@ -225,12 +250,16 @@ namespace strtpl {
225250
}
226251
throw std::runtime_error("Unrecognized group in pattern");
227252
};
253+
228254
return regex_replace_fn(s, re, convert, flags);
229255
}
230256
}; // struct string_template
231257

232258
inline namespace cpo {
233259
// See https://github.com/python/cpython/blob/971343eb569a3418aa9a0bad9b638cccf1470ef8/Lib/string.py#L57
234260
inline constexpr string_template<char> substitute{"$", "([_a-zA-Z][_a-zA-Z0-9]*)"};
261+
inline constexpr string_template<wchar_t> wsubstitute{L"$", L"([_a-zA-Z][_a-zA-Z0-9]*)"};
235262
} // namespace cpo
263+
264+
#undef TYPED_LITERAL
236265
} // namespace strtpl

tests/main/main.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,21 @@ TEST_CASE("main", "[main]") {
4040
CHECK_THROWS_AS(strtpl::substitute(s5, map), std::out_of_range);
4141
}
4242
}
43+
44+
TEST_CASE("wchar_t", "[main]") {
45+
{ // wsubstitute
46+
std::unordered_map<std::wstring_view, std::wstring_view> map{
47+
{L"what", L"example"},
48+
};
49+
std::wstring_view s1 = L"This is $what.";
50+
std::wstring_view s2 = L"This is ${what}ified.";
51+
std::wstring_view s3 = L"This is dollar $$.";
52+
std::wstring_view s4 = L"This is error $.";
53+
std::wstring_view s5 = L"This is error too $which.";
54+
CHECK(strtpl::wsubstitute(s1, map) == L"This is example.");
55+
CHECK(strtpl::wsubstitute(s2, map) == L"This is exampleified.");
56+
CHECK(strtpl::wsubstitute(s3, map) == L"This is dollar $.");
57+
CHECK_THROWS_AS(strtpl::wsubstitute(s4, map), std::runtime_error);
58+
CHECK_THROWS_AS(strtpl::wsubstitute(s5, map), std::out_of_range);
59+
}
60+
}

0 commit comments

Comments
 (0)