stdex
Additional custom or not Standard C++ covered algorithms
Loading...
Searching...
No Matches
string.hpp
1/*
2 SPDX-License-Identifier: MIT
3 Copyright © 2016-2023 Amebis
4*/
5
6#pragma once
7
8#include "sal.hpp"
9#include <assert.h>
10#include <ctype.h>
11#include <locale.h>
12#include <stdarg.h>
13#include <stdint.h>
14#include <memory>
15#include <stdexcept>
16
17namespace stdex
18{
19#ifdef _WIN32
20 using locale_t = _locale_t;
21
22 inline locale_t create_locale(_In_ int category, _In_z_ const char* locale) { return _create_locale(category, locale); }
23 inline locale_t create_locale(_In_ int category, _In_z_ const wchar_t* locale) { return _wcreate_locale(category, locale); }
24 inline void free_locale(_In_opt_ _locale_t locale) { _free_locale(locale); }
25#else
26 using locale_t = ::locale_t;
27#endif
28
33 {
37 void operator()(_In_ locale_t locale) const
38 {
39 free_locale(locale);
40 }
41 };
42
46 using locale = std::unique_ptr<__crt_locale_pointers, free_locale_delete>;
47
51 static locale locale_C(_create_locale(LC_ALL, "C"));
52
56#ifdef _WIN32
57 typedef wchar_t utf16_t;
58#else
59 typedef char16_t utf16_t;
60#endif
61
67 inline bool is_high_surrogate(_In_ utf16_t chr)
68 {
69 return 0xd800 < chr && chr < 0xdc00;
70 }
71
77 inline bool is_low_surrogate(_In_ utf16_t chr)
78 {
79 return 0xdc00 < chr && chr < 0xe000;
80 }
81
87 inline bool is_surrogate_pair(_In_reads_(2) const utf16_t* str)
88 {
89 return is_high_surrogate(str[0]) && is_low_surrogate(str[1]);
90 }
91
97 inline char32_t surrogate_pair_to_ucs4(_In_reads_(2) const utf16_t* str)
98 {
99 assert(is_surrogate_pair(str));
100 return
101 ((char32_t)(str[0] - 0xd800) << 10) +
102 (char32_t)(str[1] - 0xdc00) +
103 0x10000;
104 }
105
111 inline void ucs4_to_surrogate_pair(_Out_writes_(2) utf16_t* str, _In_ char32_t chr)
112 {
113 assert(chr >= 0x10000);
114 chr -= 0x10000;
115 str[0] = 0xd800 + (char32_t)((chr >> 10) & 0x3ff);
116 str[1] = 0xdc00 + (char32_t)(chr & 0x3ff);
117 }
118
124 inline bool iscombining(_In_ char32_t chr)
125 {
126 return
127 0x0300 <= chr && chr < 0x0370 ||
128 0x1dc0 <= chr && chr < 0x1e00 ||
129 0x20d0 <= chr && chr < 0x2100 ||
130 0xfe20 <= chr && chr < 0xfe30;
131 }
132
138 template <class T>
139 inline size_t islbreak(_In_ T chr)
140 {
141 return chr == '\n' || chr == '\r';
142 }
143
150 template <class T>
151 inline size_t islbreak(_In_reads_or_z_opt_(count) const T* chr, _In_ size_t count)
152 {
153 _Analysis_assume_(chr || !count);
154 if (count >= 2 && (chr[0] == '\r' && chr[1] == '\n' || chr[0] == '\n' && chr[1] == '\r'))
155 return 2;
156 if (count > 1 && (chr[0] == '\n' || chr[0] == '\r'))
157 return 1;
158 return 0;
159 }
160
167 inline size_t glyphlen(_In_reads_or_z_opt_(count) const wchar_t* glyph, _In_ size_t count)
168 {
169 _Analysis_assume_(glyph || !count);
170 if (count) {
171#ifdef _WIN32
172 size_t i = count < 2 || !is_surrogate_pair(glyph) ? 1 : 2;
173#else
174 size_t i = 1;
175#endif
176 for (; i < count && iscombining(glyph[i]); ++i);
177 return i;
178 }
179 return 0;
180 }
181
189 template <class T>
190 inline size_t strlen(_In_z_ const T* str)
191 {
192 assert(str);
193 size_t i;
194 for (i = 0; str[i]; ++i);
195 return i;
196 }
197
206 template <class T>
207 inline size_t strnlen(_In_reads_or_z_opt_(count) const T* str, _In_ size_t count)
208 {
209 assert(str);
210 size_t i;
211 for (i = 0; i < count && str[i]; ++i);
212 return i;
213 }
214
215 constexpr auto npos{ static_cast<size_t>(-1) };
216
226 template <class T>
227 inline size_t strnchr(
228 _In_reads_or_z_opt_(count) const T* str,
229 _In_ size_t count,
230 _In_ T chr)
231 {
232 assert(str || !count);
233 for (size_t i = 0; i < count && str[i]; ++i)
234 if (str[i] == chr) return i;
235 return npos;
236 }
237
247 template <class T>
248 inline size_t strrnchr(
249 _In_reads_or_z_opt_(count) const T* str,
250 _In_ size_t count,
251 _In_ T chr)
252 {
253 assert(str || !count);
254 size_t z = npos;
255 for (size_t i = 0; i < count && str[i]; ++i)
256 if (str[i] == chr) z = i;
257 return z;
258 }
259
269 template <class T>
270 inline size_t strnichr(
271 _In_reads_or_z_opt_(count) const T* str,
272 _In_ size_t count,
273 _In_ T chr,
274 _In_ const std::locale& locale)
275 {
276 assert(str || !count);
277 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
278 chr = ctype.tolower(chr);
279 for (size_t i = 0; i < count && str[i]; ++i)
280 if (ctype.tolower(str[i]) == chr) return i;
281 return npos;
282 }
283
293 template <class T>
294 inline size_t strrnichr(
295 _In_reads_or_z_opt_(count) const T* str,
296 _In_ size_t count,
297 _In_ T chr,
298 _In_ const std::locale& locale)
299 {
300 assert(str || !count);
301 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
302 chr = ctype.tolower(chr);
303 size_t z = npos;
304 for (size_t i = 0; i < count && str[i]; ++i)
305 if (ctype.tolower(str[i]) == chr) z = i;
306 return z;
307 }
308
319 template <class T1, class T2>
320 inline int strncmp(
321 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
322 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
323 {
324 assert(str1 || !count1);
325 assert(str2 || !count2);
326 size_t i; T1 a; T2 b;
327 for (i = 0; i < count1 && i < count2 && ((a = str1[i]) | (b = str2[i])); ++i) {
328 if (a > b) return +1;
329 if (a < b) return -1;
330 }
331 if (i < count1 && str1[i]) return +1;
332 if (i < count2 && str2[i]) return -1;
333 return 0;
334 }
335
346 template <class T>
347 inline int strncoll(
348 _In_reads_or_z_opt_(count1) const T* str1, _In_ size_t count1,
349 _In_reads_or_z_opt_(count2) const T* str2, _In_ size_t count2,
350 _In_ const std::locale& locale)
351 {
352 assert(str1 || !count1);
353 assert(str2 || !count2);
354 auto& collate = std::use_facet<std::collate<T>>(locale);
355 return collate.compare(str1, str1 + count1, str2, str2 + count2);
356 }
357
368 template <class T1, class T2>
369 inline int strnicmp(
370 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
371 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2,
372 _In_ const std::locale& locale)
373 {
374 assert(str1 || !count1);
375 assert(str2 || !count2);
376 size_t i; T1 a; T2 b;
377 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
378 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
379 for (i = 0; i < count1 && i < count2 && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); i++) {
380 if (a > b) return +1;
381 if (a < b) return -1;
382 }
383 if (i < count1 && str1[i]) return +1;
384 if (i < count2 && str2[i]) return -1;
385 return 0;
386 }
387
397 template <class T1, class T2>
398 inline size_t strnstr(
399 _In_reads_or_z_opt_(count) const T1* str,
400 _In_ size_t count,
401 _In_z_ const T2* sample)
402 {
403 assert(str || !count);
404 assert(sample);
405 for (size_t offset = 0;; ++offset) {
406 for (size_t i = offset, j = 0;; ++i, ++j) {
407 if (!sample[j])
408 return offset;
409 if (i >= count || !str[i])
410 return npos;
411 if (str[i] != sample[j])
412 break;
413 }
414 }
415 }
416
426 template <class T1, class T2>
427 inline size_t strnistr(
428 _In_reads_or_z_opt_(count) const T1* str,
429 _In_ size_t count,
430 _In_z_ const T2* sample,
431 _In_ const std::locale& locale)
432 {
433 assert(str || !count);
434 assert(sample);
435 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
436 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
437 for (size_t offset = 0;; ++offset) {
438 for (size_t i = offset, j = 0;; ++i, ++j) {
439 if (!sample[j])
440 return offset;
441 if (i >= count || !str[i])
442 return npos;
443 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
444 break;
445 }
446 }
447 }
448
457 template <class T1, class T2>
458 inline size_t strcpy(
459 _Out_writes_z_(_String_length_(src) + 1) T1* dst,
460 _In_z_ const T2* src)
461 {
462 assert(dst && src);
463 for (size_t i = 0; ; ++i) {
464 if ((dst[i] = src[i]) == 0)
465 return i;
466 }
467 }
468
478 template <class T1, class T2>
479 inline size_t strncpy(
480 _Out_writes_(count) _Post_maybez_ T1* dst,
481 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
482 {
483 assert(dst && src || !count);
484 for (size_t i = 0; ; ++i) {
485 if (i >= count)
486 return i;
487 if ((dst[i] = src[i]) == 0)
488 return i;
489 }
490 }
491
502 template <class T1, class T2>
503 inline size_t strncpy(
504 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
505 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
506 {
507 assert(dst || !count_dst);
508 assert(src || !count_src);
509 for (size_t i = 0; ; ++i)
510 {
511 if (i > count_dst)
512 return i;
513 if (i > count_src) {
514 dst[i] = 0;
515 return i;
516 }
517 if ((dst[i] = src[i]) == 0)
518 return i;
519 }
520 }
521
530 template <class T1, class T2>
531 inline size_t strcat(
532 _In_z_ _Out_writes_z_(_String_length_(dst) + _String_length_(src) + 1) T1* dst,
533 _In_z_ const T2* src)
534 {
535 assert(dst && src);
536 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
537 if ((dst[j] = src[i]) == 0)
538 return j;
539 }
540 }
541
551 template <class T1, class T2>
552 inline size_t strncat(
553 _Out_writes_(count) _Post_maybez_ T1* dst,
554 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
555 {
556 assert(dst && src || !count);
557 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
558 if (i >= count)
559 return j;
560 if ((dst[j] = src[i]) == 0)
561 return j;
562 }
563 }
564
575 template <class T1, class T2>
576 inline size_t strncat(
577 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
578 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
579 {
580 assert(dst || !count_dst);
581 assert(src || !count_src);
582 for (size_t i = 0, j = stdex::strnlen<T1>(dst, count_dst); ; ++i, ++j)
583 {
584 if (j > count_dst)
585 return j;
586 if (i > count_src) {
587 dst[j] = 0;
588 return j;
589 }
590 if ((dst[j] = src[i]) == 0)
591 return j;
592 }
593 }
594
605 template <class T>
606 inline _Check_return_ _Ret_maybenull_z_ T* strdup(_In_opt_z_ const T* str)
607 {
608 if (!str) _Unlikely_
609 return nullptr;
610 size_t count = strlen(str) + 1;
611 T* dst = new T[count];
612 strncpy(dst, count, str, SIZE_MAX);
613 return dst;
614 }
615
627 template <class T>
628 inline _Ret_z_ T* strndup(
629 _In_reads_or_z_opt_(count) const T* str,
630 _In_ size_t count)
631 {
632 T* dst = new T[count];
633 strncpy(dst, count, str, SIZE_MAX);
634 return dst;
635 }
636
646 template <class T>
647 inline size_t crlf2nl(_Out_writes_z_(strlen(src)) T* dst, _In_z_ const T* src)
648 {
649 assert(dst);
650 assert(src);
651 size_t i, j;
652 for (i = j = 0; src[j];) {
653 if (src[j] != '\r' || src[j + 1] != '\n')
654 dst[i++] = src[j++];
655 else {
656 dst[i++] = '\n';
657 j += 2;
658 }
659 }
660 dst[i] = 0;
661 return i;
662 }
663
665 template <class T, class T_bin>
666 inline T_bin strtoint(
667 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
668 _Out_opt_ size_t* end,
669 _In_ int radix,
670 _Out_ uint8_t& flags)
671 {
672 assert(str || !count);
673 assert(radix == 0 || 2 <= radix && radix <= 36);
674
675 size_t i = 0;
676 T_bin value = 0, digit,
677 max_ui = (T_bin)-1,
678 max_ui_pre1, max_ui_pre2;
679
680 flags = 0;
681
682 // Skip leading spaces.
683 for (;; ++i) {
684 if (i >= count || !str[i]) goto error;
685 if (!isspace(str[i])) break;
686 }
687
688 // Read the sign.
689 if (str[i] == '+') {
690 flags &= ~0x01;
691 ++i;
692 if (i >= count || !str[i]) goto error;
693 }
694 else if (str[i] == '-') {
695 flags |= 0x01;
696 ++i;
697 if (i >= count || !str[i]) goto error;
698 }
699
700 if (radix == 16) {
701 // On hexadecimal, allow leading 0x.
702 if (str[i] == '0' && i + 1 < count && (str[i + 1] == 'x' || str[i + 1] == 'X')) {
703 i += 2;
704 if (i >= count || !str[i]) goto error;
705 }
706 }
707 else if (!radix) {
708 // Autodetect radix.
709 if (str[i] == '0') {
710 ++i;
711 if (i >= count || !str[i]) goto error;
712 if (str[i] == 'x' || str[i] == 'X') {
713 radix = 16;
714 ++i;
715 if (i >= count || !str[i]) goto error;
716 }
717 else
718 radix = 8;
719 }
720 else
721 radix = 10;
722 }
723
724 // We have the radix.
725 max_ui_pre1 = max_ui / (T_bin)radix;
726 max_ui_pre2 = max_ui % (T_bin)radix;
727 for (;;) {
728 if ('0' <= str[i] && str[i] <= '9')
729 digit = (T_bin)str[i] - '0';
730 else if ('A' <= str[i] && str[i] <= 'Z')
731 digit = (T_bin)str[i] - 'A' + '\x0a';
732 else if ('a' <= str[i] && str[i] <= 'z')
733 digit = (T_bin)str[i] - 'a' + '\x0a';
734 else
735 goto error;
736 if (digit >= (T_bin)radix)
737 goto error;
738
739 if (value < max_ui_pre1 || // Multiplication nor addition will not overflow.
740 value == max_ui_pre1 && digit <= max_ui_pre2) // Small digits will not overflow.
741 value = value * (T_bin)radix + digit;
742 else {
743 // Overflow!
744 flags |= 0x02;
745 }
746
747 ++i;
748 if (i >= count || !str[i])
749 goto error;
750 }
751
752 error:
753 if (end) *end = i;
754 return value;
755 }
757
768 template <class T, class T_bin>
769 T_bin strtoint(
770 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
771 _Out_opt_ size_t* end,
772 _In_ int radix)
773 {
774 uint8_t flags;
775 T_bin value;
776
777 switch (sizeof(T_bin)) {
778 case 1:
779 value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags);
780 if ((flags & 0x01) && (value & 0x80)) {
781 // Sign bit is 1 => overflow.
782 flags |= 0x02;
783 }
784 return (flags & 0x02) ?
785 (flags & 0x01) ? (T_bin)0x80 : (T_bin)0x7f :
786 (flags & 0x01) ? -value : value;
787
788 case 2:
789 value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags);
790 if ((flags & 0x01) && (value & 0x8000)) {
791 // Sign bit is 1 => overflow.
792 flags |= 0x02;
793 }
794 return (flags & 0x02) ?
795 (flags & 0x01) ? (T_bin)0x8000 : (T_bin)0x7fff :
796 (flags & 0x01) ? -value : value;
797
798 case 4:
799 value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags);
800 if ((flags & 0x01) && (value & 0x80000000)) {
801 // Sign bit is 1 => overflow.
802 flags |= 0x02;
803 }
804 return (flags & 0x02) ?
805 (flags & 0x01) ? (T_bin)0x80000000 : (T_bin)0x7fffffff :
806 (flags & 0x01) ? -value : value;
807
808 case 8:
809 value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags);
810 if ((flags & 0x01) && (value & 0x8000000000000000)) {
811 // Sign bit is 1 => overflow.
812 flags |= 0x02;
813 }
814 return (flags & 0x02) ?
815 (flags & 0x01) ? (T_bin)0x8000000000000000 : (T_bin)0x7fffffffffffffff :
816 (flags & 0x01) ? -value : value;
817
818 default:
819 throw std::invalid_argument("Unsupported bit length");
820 }
821 }
822
833 template <class T, class T_bin>
834 inline T_bin strtouint(
835 _In_reads_or_z_opt_(count) const T* str,
836 _In_ size_t count,
837 _Out_opt_ size_t* end,
838 _In_ int radix)
839 {
840 uint8_t flags;
841 T_bin value;
842
843 switch (sizeof(T_bin)) {
844 case 1: value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags); break;
845 case 2: value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags); break;
846 case 4: value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags); break;
847 case 8: value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags); break;
848 default: throw std::invalid_argument("Unsupported bit length");
849 }
850
851 return (flags & 0x02) ?
852 (flags & 0x01) ? (T_bin)0 : (T_bin)-1 :
853 (flags & 0x01) ? ~value : value;
854 }
855
866 template <class T>
867 inline int32_t strto32(
868 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
869 _Out_opt_ size_t* end,
870 _In_ int radix)
871 {
872 return strtoint<T, int32_t>(str, count, end, radix);
873 }
874
885 template <class T>
886 inline int64_t strto64(
887 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
888 _Out_opt_ size_t* end,
889 _In_ int radix)
890 {
891 return strtoint<T, int64_t>(str, count, end, radix);
892 }
893
905 template <class T>
906 inline intptr_t strtoi(
907 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
908 _Out_opt_ size_t* end,
909 _In_ int radix)
910 {
911#if defined(_WIN64) || defined(__LP64__)
912 return (intptr_t)strto64(str, count, end, radix);
913#else
914 return (intptr_t)strto32(str, count, end, radix);
915#endif
916 }
917
928 template <class T>
929 inline uint32_t strtou32(
930 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
931 _Out_opt_ size_t* end,
932 _In_ int radix)
933 {
934 return strtouint<T, uint32_t>(str, count, end, radix);
935 }
936
947 template <class T>
948 inline uint64_t strtou64(
949 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
950 _Out_opt_ size_t* end,
951 _In_ int radix)
952 {
953 return strtouint<T, uint64_t>(str, count, end, radix);
954 }
955
967 template <class T>
968 inline size_t strtoui(
969 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
970 _Out_opt_ size_t* end,
971 _In_ int radix)
972 {
973#if defined(_WIN64) || defined(__LP64__)
974 return (size_t)strtou64(str, count, end, radix);
975#else
976 return (size_t)strtou32(str, count, end, radix);
977#endif
978 }
979
981 inline int vsnprintf(_Out_z_cap_(capacity) char *str, _In_ size_t capacity, _In_z_ _Printf_format_string_params_(2) const char *format, _In_opt_ locale_t locale, _In_ va_list arg)
982 {
983 int r;
984#ifdef _WIN32
985 // Don't use _vsnprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
986#pragma warning(suppress: 4996)
987 r = _vsnprintf_l(str, capacity, format, locale, arg);
988#else
989 r = vsnprintf(str, capacity, format, arg);
990#endif
991 if (r == -1 && strnlen(str, capacity) == capacity) {
992 // Buffer overrun. Estimate buffer size for the next iteration.
993 capacity += std::max<size_t>(capacity / 8, 0x80);
994 if (capacity > INT_MAX)
995 throw std::invalid_argument("string too big");
996 return (int)capacity;
997 }
998 return r;
999 }
1000
1001 inline int vsnprintf(_Out_z_cap_(capacity) wchar_t *str, _In_ size_t capacity, _In_z_ _Printf_format_string_params_(2) const wchar_t *format, _In_opt_ locale_t locale, _In_ va_list arg)
1002 {
1003 int r;
1004
1005#ifdef _WIN32
1006 // Don't use _vsnwprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
1007#pragma warning(suppress: 4996)
1008 r = _vsnwprintf_l(str, capacity, format, locale, arg);
1009#else
1010 r = vswprintf(str, capacity, format, arg);
1011#endif
1012 if (r == -1 && strnlen(str, capacity) == capacity) {
1013 // Buffer overrun. Estimate buffer size for the next iteration.
1014 capacity += std::max<size_t>(capacity / 8, 0x80);
1015 if (capacity > INT_MAX)
1016 throw std::invalid_argument("string too big");
1017 return (int)capacity;
1018 }
1019 return r;
1020 }
1022
1031 template<class _Elem, class _Traits, class _Ax>
1032 inline void vappendf(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &str, _In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, _In_ va_list arg)
1033 {
1034 _Elem buf[1024/sizeof(_Elem)];
1035
1036 // Try with stack buffer first.
1037 int count = vsnprintf(buf, _countof(buf) - 1, format, locale, arg);
1038 if (count >= 0) {
1039 // Copy from stack.
1040 str.append(buf, count);
1041 } else {
1042 for (size_t capacity = 2*1024/sizeof(_Elem);; capacity *= 2) {
1043 // Allocate on heap and retry.
1044 auto buf_dyn = std::make_unique<_Elem[]>(capacity);
1045 count = vsnprintf(buf_dyn.get(), capacity - 1, format, locale, arg);
1046 if (count >= 0) {
1047 str.append(buf_dyn.get(), count);
1048 break;
1049 }
1050 }
1051 }
1052 }
1053
1061 template<class _Elem, class _Traits, class _Ax>
1062 inline void appendf(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &str, _In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, ...)
1063 {
1064 va_list arg;
1065 va_start(arg, locale);
1066 vappendf(str, format, locale, arg);
1067 va_end(arg);
1068 }
1069
1078 template<class _Elem, class _Traits, class _Ax>
1079 inline void vsprintf(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &str, _In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, _In_ va_list arg)
1080 {
1081 str.clear();
1082 vappendf(str, format, locale, arg);
1083 }
1084
1092 template<class _Elem, class _Traits, class _Ax>
1093 inline void sprintf(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &str, _In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, ...)
1094 {
1095 va_list arg;
1096 va_start(arg, locale);
1097 vsprintf(str, format, locale, arg);
1098 va_end(arg);
1099 }
1100
1110 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1111 inline std::basic_string<_Elem, _Traits, _Ax> vsprintf(_In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, _In_ va_list arg)
1112 {
1113 std::basic_string<_Elem, _Traits, _Ax> str;
1114 vappendf(str, format, locale, arg);
1115 return str;
1116 }
1117
1126 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1127 inline std::basic_string<_Elem, _Traits, _Ax> sprintf(_In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, ...)
1128 {
1129 va_list arg;
1130 va_start(arg, locale);
1131 auto str = vsprintf(format, locale, arg);
1132 va_end(arg);
1133 return str;
1134 }
1135}
Deleter for unique_ptr using free_locale.
Definition string.hpp:33
void operator()(locale_t locale) const
Delete a pointer.
Definition string.hpp:37