neoGFX
Cross-platform C++ app/game engine
Loading...
Searching...
No Matches
string_utf.hpp
Go to the documentation of this file.
1// string_utf.hpp
2/*
3 * Copyright (c) 2007 Leigh Johnston.
4 *
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * * Neither the name of Leigh Johnston nor the names of any
19 * other contributors to this software may be used to endorse or
20 * promote products derived from this software without specific prior
21 * written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
24 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34*/
35
36#pragma once
37
38#include <neolib/neolib.hpp>
39#include <algorithm>
40#include <string>
41#include <map>
42#include <vector>
43#include <sstream>
44#include <cstdlib>
45#ifndef __clang__
46#include <cuchar>
47#else
48#include <uchar.h>
49namespace std
50{
51 using ::mbstate_t;
52 using ::size_t;
53 using ::mbrtoc16;
54 using ::c16rtomb;
55 using ::mbrtoc32;
56 using ::c32rtomb;
57}
58#endif
59#include <cwchar>
60#include <cctype>
61#include <cwctype>
62#include <cassert>
63#include <boost/locale.hpp>
65
66namespace neolib
67{
68 typedef char32_t unicode_char_t;
69 const char INVALID_CHAR8 = '?';
70 const unicode_char_t INVALID_CHAR32 = static_cast<unicode_char_t>(0xFFFD);
71
72 namespace utf16
73 {
74 inline bool is_high_surrogate(unicode_char_t aCharacter)
75 {
76 return aCharacter >= 0xD800 && aCharacter <= 0xDBFF;
77 }
78 inline bool is_low_surrogate(unicode_char_t aCharacter)
79 {
80 return aCharacter >= 0xDC00 && aCharacter <= 0xDFFF;
81 }
82 inline bool is_surrogate_pair(unicode_char_t aHighValue, unicode_char_t aLowValue)
83 {
84 return is_high_surrogate(aHighValue) && is_low_surrogate(aLowValue);
85 }
86 }
87
88 inline std::size_t append_utf8(std::string& aString, unicode_char_t aCharacter)
89 {
90 if (aCharacter <= 0x7F)
91 {
92 aString.append(1, static_cast<char>(aCharacter));
93 return 1;
94 }
95 else if (aCharacter <= 0x7FF)
96 {
97 aString.append(1, static_cast<char>(((aCharacter >> 6) & 0x1F) | 0xC0));
98 aString.append(1, static_cast<char>((aCharacter & 0x3F) | 0x80));
99 return 2;
100 }
101 else if (aCharacter <= 0xFFFF)
102 {
103 aString.append(1, static_cast<char>(((aCharacter >> 12) & 0x0F) | 0xE0));
104 aString.append(1, static_cast<char>(((aCharacter >> 6 ) & 0x3F) | 0x80));
105 aString.append(1, static_cast<char>((aCharacter& 0x3F) | 0x80));
106 return 3;
107 }
108 else if (aCharacter <= 0x10FFFF)
109 {
110 aString.append(1, static_cast<char>(((aCharacter >> 18) & 0x07) | 0xF0));
111 aString.append(1, static_cast<char>(((aCharacter >> 12 ) & 0x3F) | 0x80));
112 aString.append(1, static_cast<char>(((aCharacter >> 6 ) & 0x3F) | 0x80));
113 aString.append(1, static_cast<char>((aCharacter& 0x3F) | 0x80));
114 return 4;
115 }
116 else
117 {
118 aString.append(1, INVALID_CHAR8);
119 return 1;
120 }
121 }
122
123 typedef std::map<std::string::size_type, std::u16string::size_type> utf16_to_utf8_character_map;
124
125 namespace detail
126 {
128 {
129 struct short_narrow_string : std::logic_error { short_narrow_string() : std::logic_error("neolib::detail::character_map_updater::short_narrow_string") {} };
132 void operator()(std::u16string::size_type aFrom, bool aSurrogatePair, const std::string& aNarrowString, std::string::size_type aNumberAdded)
133 {
134 for (std::string::size_type i = 0; i < aNumberAdded; ++i)
135 iCharMap[aNarrowString.size() - aNumberAdded + i] = aFrom;
136 if (aSurrogatePair && aNarrowString.size())
137 {
138 if (aNarrowString.size() <= 1)
139 throw short_narrow_string();
140 iCharMap[aNarrowString.size() - 1] = aFrom + 1;
141 }
142 }
143 };
145 {
147 void operator()(std::u16string::size_type, bool, const std::string&, std::string::size_type) {}
148 };
149 }
150
151 template <bool AllowUpper128, typename CharacterMapUpdater>
152 inline std::string utf16_to_utf8(const std::u16string& aString, CharacterMapUpdater aCharacterMapUpdater)
153 {
154 bool previousWasUtf8Prefix = false;
155 std::string narrowString;
156 std::u16string::size_type from = 0;
157 for (std::u16string::const_iterator i = aString.begin(); i != aString.end(); from = i - aString.begin())
158 {
159 bool sequenceCheck = previousWasUtf8Prefix;
160 previousWasUtf8Prefix = false;
161 unicode_char_t uch = *i++;
162 bool surrogatePair = false;
163 if (utf16::is_high_surrogate(uch) && i != aString.end() && utf16::is_surrogate_pair(uch, *i))
164 {
165 uch = ((uch & 0x3FF) << 10);
166 uch = uch | (*i++ & 0x3FF);
167 uch += 0x10000;
168 surrogatePair = true;
169 }
170 else if (AllowUpper128)
171 {
172 int narrowChar = wctob(static_cast<wint_t>(uch));
173 if (narrowChar != static_cast<int>(EOF) && narrowChar != static_cast<int>(WEOF) && static_cast<unsigned char>(narrowChar) > 0x7Fu)
174 {
175 unsigned char nch = static_cast<unsigned char>(narrowChar);
176 if ((nch & 0xE0) == 0xC0 || (nch & 0xF0) == 0xE0 || (nch & 0xF8) == 0xF0)
177 {
178 previousWasUtf8Prefix = true;
179 }
180 else if (sequenceCheck && (nch & 0xC0) == 0x80)
181 {
182 int previousNarrowChar = static_cast<int>(narrowString[narrowString.size()-1]);
183 narrowString.erase(narrowString.size() - 1);
184 aCharacterMapUpdater(from, surrogatePair, narrowString, append_utf8(narrowString, static_cast<unicode_char_t>(btowc(previousNarrowChar))));
185 }
186 narrowString.append(1, static_cast<char>(narrowChar));
187 aCharacterMapUpdater(from, surrogatePair, narrowString, 1);
188 continue;
189 }
190 }
191 aCharacterMapUpdater(from, surrogatePair, narrowString, append_utf8(narrowString, uch));
192 }
193 return narrowString;
194 }
195
196 template <bool AllowUpper128>
197 inline std::string utf16_to_utf8(const std::u16string& aString)
198 {
199 return utf16_to_utf8<AllowUpper128>(aString, detail::no_character_map_updater());
200 }
201
202 template <bool AllowUpper128>
203 inline std::string utf16_to_utf8(const std::u16string& aString, utf16_to_utf8_character_map& aCharMap)
204 {
205 return utf16_to_utf8<AllowUpper128>(aString, detail::character_map_updater(aCharMap));
206 }
207
208 inline std::string utf16_to_utf8(const std::u16string& aString)
209 {
210 return utf16_to_utf8<false>(aString);
211 }
212
213 inline std::string utf16_to_utf8(const std::u16string& aString, utf16_to_utf8_character_map& aCharMap)
214 {
215 return utf16_to_utf8<false>(aString, aCharMap);
216 }
217
218 namespace detail
219 {
220 template <typename FwdIter>
221 inline unicode_char_t next_utf_bits(unicode_char_t aUnicodeChar, std::size_t aCount, FwdIter& aCurrent, FwdIter aEnd)
222 {
223 unicode_char_t unicodeChar = aUnicodeChar;
224 FwdIter start = aCurrent;
225 for (std::size_t i = 0; i != aCount; ++i)
226 {
227 ++aCurrent;
228 if (aCurrent == aEnd)
229 {
230 aCurrent = start;
231 return INVALID_CHAR32;
232 }
233 unsigned char nch = static_cast<unsigned char>(*aCurrent);
234 if (nch == 0xC0 || nch == 0xC1)
235 {
236 aCurrent = start;
237 return INVALID_CHAR32;
238 }
239 if ((nch & 0xC0) == 0x80)
240 unicodeChar = (unicodeChar << 6) | (static_cast<unicode_char_t>(nch & ~0xC0));
241 else
242 {
243 aCurrent = start;
244 return INVALID_CHAR32;
245 }
246 }
247 static const unicode_char_t sMaxCodePoint[] = { 0x7F, 0x7FF, 0xFFFF, 0x10FFFF };
248 if (unicodeChar <= sMaxCodePoint[aCount - 1]) // overlong sequences
249 {
250 aCurrent = start;
251 return INVALID_CHAR32;
252 }
253 return unicodeChar;
254 }
255 inline void default_utf16_conversion_callback(std::string::size_type /*aFrom*/, std::u16string::size_type /*aTo*/) {}
256 inline void default_utf32_conversion_callback(std::string::size_type /*aFrom*/, std::u32string::size_type /*aTo*/) {}
257 }
258
259 template <typename Callback>
260 inline std::u16string utf8_to_utf16(const std::string& aString, Callback aCallback, bool aCodePageFallback = false)
261 {
262 std::u16string utf16String;
263 for (std::string::const_iterator i = aString.begin(); i != aString.end();)
264 {
265 aCallback(i - aString.begin(), utf16String.size());
266 unsigned char nch = static_cast<unsigned char>(*i);
267 unicode_char_t uch = 0;
268 if ((nch & 0x80) == 0)
269 uch = static_cast<unicode_char_t>(nch & 0x7F);
270 else
271 {
272 std::string::const_iterator old = i;
273 if (nch == 0xC0 || nch == 0xC1)
274 uch = INVALID_CHAR32;
275 else if ((nch & 0xE0) == 0xC0)
276 uch = detail::next_utf_bits(static_cast<unicode_char_t>(nch & ~0xE0), 1, i, aString.end());
277 else if ((nch & 0xF0) == 0xE0)
278 uch = detail::next_utf_bits(static_cast<unicode_char_t>(nch & ~0xF0), 2, i, aString.end());
279 else if ((nch & 0xF8) == 0xF0)
280 uch = detail::next_utf_bits(static_cast<unicode_char_t>(nch & ~0xF8), 3, i, aString.end());
281 else
282 uch = INVALID_CHAR32;
283 if (i == old && aCodePageFallback)
284 {
285 wchar_t wch;
286 std::mbstate_t state = std::mbstate_t{};
287 if (std::mbrtowc(&wch, reinterpret_cast<char*>(&nch), 1, &state) == 1)
288 uch = static_cast<unicode_char_t>(wch);
289 else
290 uch = static_cast<unicode_char_t>(nch);
291 }
292 }
293
294 if (uch <= 0xFFFF)
295 utf16String.append(1, static_cast<char16_t>(uch));
296 else
297 {
298 // UTF-16 bit...
299 uch -= 0x10000;
300 utf16String.append(1, static_cast<char16_t>(0xd800|(uch >> 10)));
301 utf16String.append(1, static_cast<char16_t>(0xdc00|(uch & 0x3FF)));
302 }
303
304 if (i != aString.end())
305 ++i;
306 }
307 return utf16String;
308 }
309
310 inline std::u16string utf8_to_utf16(const std::string& aString, bool aCodePageFallback = false)
311 {
312 return utf8_to_utf16(aString, detail::default_utf16_conversion_callback, aCodePageFallback);
313 }
314
315 template <typename Callback>
316 inline std::u32string utf8_to_utf32(std::string_view const& aStringView, Callback aCallback, bool aCodePageFallback = false)
317 {
318 auto begin = aStringView.begin();
319 auto end = aStringView.end();
320 std::u32string utf32String;
321 for (auto i = begin; i != end; ++i)
322 {
323 aCallback(i - begin, utf32String.size());
324
325 unsigned char nch = static_cast<unsigned char>(*i);
326 unicode_char_t uch = 0;
327 if ((nch & 0x80) == 0)
328 uch = static_cast<unicode_char_t>(nch & 0x7F);
329 else
330 {
331 auto old = i;
332 if (nch == 0xC0 || nch == 0xC1)
333 uch = INVALID_CHAR32;
334 else if ((nch & 0xE0) == 0xC0)
335 uch = detail::next_utf_bits(static_cast<unicode_char_t>(nch & ~0xE0), 1, i, end);
336 else if ((nch & 0xF0) == 0xE0)
337 uch = detail::next_utf_bits(static_cast<unicode_char_t>(nch & ~0xF0), 2, i, end);
338 else if ((nch & 0xF8) == 0xF0)
339 uch = detail::next_utf_bits(static_cast<unicode_char_t>(nch & ~0xF8), 3, i, end);
340 else
341 uch = INVALID_CHAR32;
342 if (i == old && aCodePageFallback)
343 {
344 char32_t ch32;
345 std::mbstate_t state = std::mbstate_t{};
346 if (std::mbrtoc32(&ch32, reinterpret_cast<char*>(&nch), 1, &state) == 1)
347 uch = static_cast<unicode_char_t>(ch32);
348 else
349 uch = static_cast<unicode_char_t>(nch);
350 }
351 }
352
353 utf32String.append(1, uch);
354 }
355 return utf32String;
356 }
357
358 template <typename Callback>
359 inline std::u32string utf8_to_utf32(std::string::const_iterator aBegin, std::string::const_iterator aEnd, Callback aCallback, bool aCodePageFallback = false)
360 {
362 if (aBegin == aEnd)
363 return std::u32string{};
364 return utf8_to_utf32(std::string_view{ &*aBegin, static_cast<std::string_view::size_type>(std::distance(aBegin, aEnd)) }, aCallback, aCodePageFallback);
365 }
366
367 template <typename Callback>
368 inline std::u32string utf8_to_utf32(const std::string& aString, Callback aCallback, bool aCodePageFallback = false)
369 {
370 return utf8_to_utf32(aString.begin(), aString.end(), aCallback, aCodePageFallback);
371 }
372
373 inline std::u32string utf8_to_utf32(std::string::const_iterator aBegin, std::string::const_iterator aEnd, bool aCodePageFallback = false)
374 {
375 return utf8_to_utf32(aBegin, aEnd, detail::default_utf32_conversion_callback, aCodePageFallback);
376 }
377
378 inline std::u32string utf8_to_utf32(const std::string& aString, bool aCodePageFallback = false)
379 {
380 return utf8_to_utf32(aString.begin(), aString.end(), aCodePageFallback);
381 }
382
383 inline std::u32string utf8_to_utf32(const std::string_view& aStringView, bool aCodePageFallback = false)
384 {
385 return utf8_to_utf32(aStringView, detail::default_utf32_conversion_callback, aCodePageFallback);
386 }
387
388 inline std::string utf32_to_utf8(const std::u32string& aString)
389 {
390 std::string result;
391 for (auto ch : aString)
392 append_utf8(result, ch);
393 return result;
394 }
395
396 inline bool is_utf8_trailing(char aCharacter)
397 {
398 return (aCharacter & 0xC0) == 0x80;
399 }
400
401 template <typename CharT, typename Traits>
402 inline bool check_utf8(const std::basic_string_view<CharT, Traits>& aString)
403 {
404 auto end = aString.end();
405 for (auto i = aString.begin(); i != end; ++i)
406 {
407 unsigned char nch = static_cast<unsigned char>(*i);
408 unicode_char_t uch = 0;
409 if ((nch & 0x80) == 0)
410 uch = static_cast<unicode_char_t>(nch & 0x7F);
411 else
412 {
413 auto old = i;
414 if (nch == 0xC0 || nch == 0xC1)
415 uch = INVALID_CHAR32;
416 else if ((nch & 0xE0) == 0xC0)
417 uch = detail::next_utf_bits(static_cast<unicode_char_t>(nch & ~0xE0), 1, i, end);
418 else if ((nch & 0xF0) == 0xE0)
419 uch = detail::next_utf_bits(static_cast<unicode_char_t>(nch & ~0xF0), 2, i, end);
420 else if ((nch & 0xF8) == 0xF0)
421 uch = detail::next_utf_bits(static_cast<unicode_char_t>(nch & ~0xF8), 3, i, end);
422 else
423 uch = INVALID_CHAR32;
424 if (i == old || uch == INVALID_CHAR32)
425 return false;
426 }
427 }
428 return true;
429 }
430
431 template <typename CharT, typename Traits, typename Alloc>
432 inline bool check_utf8(const std::basic_string<CharT, Traits, Alloc>& aString)
433 {
434 return check_utf8(std::basic_string_view<CharT, Traits>{ aString });
435 }
436
437 inline bool check_utf8(const i_string& aString)
438 {
439 return check_utf8(aString.to_std_string_view());
440 }
441
442 template <typename StringT>
443 inline StringT utf16_to_any(const std::u16string& aString)
444 {
445 return utf16_to_utf8(aString);
446 }
447
448 template <>
449 inline std::u16string utf16_to_any<std::u16string>(const std::u16string& aString)
450 {
451 return aString;
452 }
453
454 template <typename StringT>
455 inline StringT utf8_to_any(const std::string& aString, bool aCodePageFallback = false)
456 {
457 return utf8_to_utf16(aString, aCodePageFallback);
458 }
459
460 template <>
461 inline std::string utf8_to_any<std::string>(const std::string& aString, bool)
462 {
463 return aString;
464 }
465
466 inline std::u16string any_to_utf16(const std::string& aString, bool aCodePageFallback = false)
467 {
468 return utf8_to_utf16(aString, aCodePageFallback);
469 }
470
471 inline const std::string& any_to_utf8(const std::string& aString)
472 {
473 return aString;
474 }
475
476 inline std::string any_to_utf8(const std::u16string& aString)
477 {
478 return utf16_to_utf8(aString);
479 }
480
481 inline const std::u16string& any_to_utf16(const std::u16string& aString)
482 {
483 return aString;
484 }
485
486 template <typename StringT>
488 {
489 public:
490 any_to_utf16_result(const typename StringT::value_type* aString, typename StringT::size_type aStringLength, bool aCodePageFallback = false) :
491 iString(utf8_to_utf16(StringT(aString, aStringLength), aCodePageFallback))
492 {
493 }
494 public:
495 const char16_t* data() const
496 {
497 return iString.data();
498 }
499 std::u16string::size_type length() const
500 {
501 return iString.length();
502 }
503 private:
504 const std::u16string iString;
505 };
506
507 template <>
508 class any_to_utf16_result<std::u16string>
509 {
510 public:
511 any_to_utf16_result(const char16_t* aString, std::u16string::size_type aStringLength) :
512 iString(aString),
513 iStringLength(aStringLength)
514 {
515 }
516 public:
517 const char16_t* data() const
518 {
519 return iString;
520 }
521 std::u16string::size_type length() const
522 {
523 return iStringLength;
524 }
525 private:
526 const char16_t* iString;
527 std::u16string::size_type iStringLength;
528 };
529
530 inline any_to_utf16_result<std::string> any_to_utf16(const std::string::value_type* aString, std::string::size_type aStringLength)
531 {
532 return any_to_utf16_result<std::string>(aString, aStringLength);
533 }
534
535 inline any_to_utf16_result<std::u16string> any_to_utf16(const std::u16string::value_type* aString, std::u16string::size_type aStringLength)
536 {
537 return any_to_utf16_result<std::u16string>(aString, aStringLength);
538 }
539
540 template <typename CharT, typename Traits, typename Alloc>
541 inline std::string utf16_to_narrow(const std::basic_string<CharT, Traits, Alloc>& aWideString)
542 {
543 std::vector<char> narrowString;
544 narrowString.resize(aWideString.size() + 1);
545 wcstombs(&narrowString[0], aWideString.c_str(), aWideString.size() + 1);
546 return std::string(&narrowString[0]);
547 }
548
549 template <typename CharT, typename Traits, typename Alloc>
550 inline std::u16string narrow_to_utf16(const std::basic_string<CharT, Traits, Alloc>& aNarrowString)
551 {
552 std::vector<char16_t> utf16String;
553 utf16String.resize(aNarrowString.size() + 1);
554 mbstowcs(&utf16String[0], aNarrowString.c_str(), aNarrowString.size() + 1);
555 return std::u16string(&utf16String[0]);
556 }
557}
any_to_utf16_result(const char16_t *aString, std::u16string::size_type aStringLength)
std::u16string::size_type length() const
std::u16string::size_type length() const
const char16_t * data() const
any_to_utf16_result(const typename StringT::value_type *aString, typename StringT::size_type aStringLength, bool aCodePageFallback=false)
std::string_view to_std_string_view() const noexcept
Definition i_string.hpp:76
void default_utf16_conversion_callback(std::string::size_type, std::u16string::size_type)
unicode_char_t next_utf_bits(unicode_char_t aUnicodeChar, std::size_t aCount, FwdIter &aCurrent, FwdIter aEnd)
void default_utf32_conversion_callback(std::string::size_type, std::u32string::size_type)
bool is_surrogate_pair(unicode_char_t aHighValue, unicode_char_t aLowValue)
bool is_low_surrogate(unicode_char_t aCharacter)
bool is_high_surrogate(unicode_char_t aCharacter)
std::map< std::string::size_type, std::u16string::size_type > utf16_to_utf8_character_map
StringT utf8_to_any(const std::string &aString, bool aCodePageFallback=false)
const unicode_char_t INVALID_CHAR32
bool is_utf8_trailing(char aCharacter)
bool check_utf8(const std::basic_string_view< CharT, Traits > &aString)
std::string utf32_to_utf8(const std::u32string &aString)
std::u16string narrow_to_utf16(const std::basic_string< CharT, Traits, Alloc > &aNarrowString)
const std::string & any_to_utf8(const std::string &aString)
std::string utf16_to_narrow(const std::basic_string< CharT, Traits, Alloc > &aWideString)
std::string utf16_to_utf8(const std::u16string &aString, CharacterMapUpdater aCharacterMapUpdater)
std::u16string any_to_utf16(const std::string &aString, bool aCodePageFallback=false)
StringT utf16_to_any(const std::u16string &aString)
std::size_t append_utf8(std::string &aString, unicode_char_t aCharacter)
std::u32string utf8_to_utf32(std::string_view const &aStringView, Callback aCallback, bool aCodePageFallback=false)
char32_t unicode_char_t
const char INVALID_CHAR8
std::string utf8_to_any< std::string >(const std::string &aString, bool)
std::u16string utf16_to_any< std::u16string >(const std::u16string &aString)
std::u16string utf8_to_utf16(const std::string &aString, Callback aCallback, bool aCodePageFallback=false)
Definition plf_hive.h:79
iterator_traits< it_type >::difference_type distance(const it_type first, const it_type last)
Definition plf_hive.h:107
character_map_updater(utf16_to_utf8_character_map &aCharMap)
void operator()(std::u16string::size_type aFrom, bool aSurrogatePair, const std::string &aNarrowString, std::string::size_type aNumberAdded)
utf16_to_utf8_character_map & iCharMap
void operator()(std::u16string::size_type, bool, const std::string &, std::string::size_type)