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
25 struct free_locale_delete
26 {
30 void operator()(_In_ locale_t locale) const
31 {
32 _free_locale(locale);
33 }
34 };
35
36 static std::unique_ptr<__crt_locale_pointers, free_locale_delete> locale_C(_create_locale(LC_ALL, "C"));
37#else
38 using locale_t = ::locale_t;
39#endif
40
44#ifdef _WIN32
45 typedef wchar_t utf16_t;
46#else
47 typedef char16_t utf16_t;
48#endif
49
55 inline bool is_high_surrogate(_In_ utf16_t chr)
56 {
57 return 0xd800 < chr && chr < 0xdc00;
58 }
59
65 inline bool is_low_surrogate(_In_ utf16_t chr)
66 {
67 return 0xdc00 < chr && chr < 0xe000;
68 }
69
75 inline bool is_surrogate_pair(_In_reads_(2) const utf16_t* str)
76 {
77 return is_high_surrogate(str[0]) && is_low_surrogate(str[1]);
78 }
79
85 inline char32_t surrogate_pair_to_ucs4(_In_reads_(2) const utf16_t* str)
86 {
87 assert(is_surrogate_pair(str));
88 return
89 ((char32_t)(str[0] - 0xd800) << 10) +
90 (char32_t)(str[1] - 0xdc00) +
91 0x10000;
92 }
93
99 inline void ucs4_to_surrogate_pair(_Out_writes_(2) utf16_t* str, _In_ char32_t chr)
100 {
101 assert(chr >= 0x10000);
102 chr -= 0x10000;
103 str[0] = 0xd800 + (char32_t)((chr >> 10) & 0x3ff);
104 str[1] = 0xdc00 + (char32_t)(chr & 0x3ff);
105 }
106
112 inline bool iscombining(_In_ char32_t chr)
113 {
114 return
115 0x0300 <= chr && chr < 0x0370 ||
116 0x1dc0 <= chr && chr < 0x1e00 ||
117 0x20d0 <= chr && chr < 0x2100 ||
118 0xfe20 <= chr && chr < 0xfe30;
119 }
120
126 template <class T>
127 inline size_t islbreak(_In_ T chr)
128 {
129 return chr == '\n' || chr == '\r';
130 }
131
138 template <class T>
139 inline size_t islbreak(_In_reads_or_z_opt_(count) const T* chr, _In_ size_t count)
140 {
141 _Analysis_assume_(chr || !count);
142 if (count >= 2 && (chr[0] == '\r' && chr[1] == '\n' || chr[0] == '\n' && chr[1] == '\r'))
143 return 2;
144 if (count > 1 && (chr[0] == '\n' || chr[0] == '\r'))
145 return 1;
146 return 0;
147 }
148
155 inline size_t glyphlen(_In_reads_or_z_opt_(count) const wchar_t* glyph, _In_ size_t count)
156 {
157 _Analysis_assume_(glyph || !count);
158 if (count) {
159#ifdef _WIN32
160 size_t i = count < 2 || !is_surrogate_pair(glyph) ? 1 : 2;
161#else
162 size_t i = 1;
163#endif
164 for (; i < count && iscombining(glyph[i]); ++i);
165 return i;
166 }
167 return 0;
168 }
169
177 template <class T>
178 inline size_t strlen(_In_z_ const T* str)
179 {
180 assert(str);
181 size_t i;
182 for (i = 0; str[i]; ++i);
183 return i;
184 }
185
194 template <class T>
195 inline size_t strnlen(_In_reads_or_z_opt_(count) const T* str, _In_ size_t count)
196 {
197 assert(str);
198 size_t i;
199 for (i = 0; i < count && str[i]; ++i);
200 return i;
201 }
202
203 constexpr auto npos{ static_cast<size_t>(-1) };
204
214 template <class T>
215 inline size_t strnchr(
216 _In_reads_or_z_opt_(count) const T* str,
217 _In_ size_t count,
218 _In_ T chr)
219 {
220 assert(str || !count);
221 for (size_t i = 0; i < count && str[i]; ++i)
222 if (str[i] == chr) return i;
223 return npos;
224 }
225
235 template <class T>
236 inline size_t strrnchr(
237 _In_reads_or_z_opt_(count) const T* str,
238 _In_ size_t count,
239 _In_ T chr)
240 {
241 assert(str || !count);
242 size_t z = npos;
243 for (size_t i = 0; i < count && str[i]; ++i)
244 if (str[i] == chr) z = i;
245 return z;
246 }
247
257 template <class T>
258 inline size_t strnichr(
259 _In_reads_or_z_opt_(count) const T* str,
260 _In_ size_t count,
261 _In_ T chr,
262 _In_ const std::locale& locale)
263 {
264 assert(str || !count);
265 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
266 chr = ctype.tolower(chr);
267 for (size_t i = 0; i < count && str[i]; ++i)
268 if (ctype.tolower(str[i]) == chr) return i;
269 return npos;
270 }
271
281 template <class T>
282 inline size_t strrnichr(
283 _In_reads_or_z_opt_(count) const T* str,
284 _In_ size_t count,
285 _In_ T chr,
286 _In_ const std::locale& locale)
287 {
288 assert(str || !count);
289 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
290 chr = ctype.tolower(chr);
291 size_t z = npos;
292 for (size_t i = 0; i < count && str[i]; ++i)
293 if (ctype.tolower(str[i]) == chr) z = i;
294 return z;
295 }
296
307 template <class T1, class T2>
308 inline int strncmp(
309 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
310 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
311 {
312 assert(str1 || !count1);
313 assert(str2 || !count2);
314 size_t i; T1 a; T2 b;
315 for (i = 0; i < count1 && i < count2 && ((a = str1[i]) | (b = str2[i])); ++i) {
316 if (a > b) return +1;
317 if (a < b) return -1;
318 }
319 if (i < count1 && str1[i]) return +1;
320 if (i < count2 && str2[i]) return -1;
321 return 0;
322 }
323
334 template <class T>
335 inline int strncoll(
336 _In_reads_or_z_opt_(count1) const T* str1, _In_ size_t count1,
337 _In_reads_or_z_opt_(count2) const T* str2, _In_ size_t count2,
338 _In_ const std::locale& locale)
339 {
340 assert(str1 || !count1);
341 assert(str2 || !count2);
342 auto& collate = std::use_facet<std::collate<T>>(locale);
343 return collate.compare(str1, str1 + count1, str2, str2 + count2);
344 }
345
356 template <class T1, class T2>
357 inline int strnicmp(
358 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
359 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2,
360 _In_ const std::locale& locale)
361 {
362 assert(str1 || !count1);
363 assert(str2 || !count2);
364 size_t i; T1 a; T2 b;
365 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
366 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
367 for (i = 0; i < count1 && i < count2 && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); i++) {
368 if (a > b) return +1;
369 if (a < b) return -1;
370 }
371 if (i < count1 && str1[i]) return +1;
372 if (i < count2 && str2[i]) return -1;
373 return 0;
374 }
375
385 template <class T1, class T2>
386 inline size_t strnstr(
387 _In_reads_or_z_opt_(count) const T1* str,
388 _In_ size_t count,
389 _In_z_ const T2* sample)
390 {
391 assert(str || !count);
392 assert(sample);
393 for (size_t offset = 0;; ++offset) {
394 for (size_t i = offset, j = 0;; ++i, ++j) {
395 if (!sample[j])
396 return offset;
397 if (i >= count || !str[i])
398 return npos;
399 if (str[i] != sample[j])
400 break;
401 }
402 }
403 }
404
414 template <class T1, class T2>
415 inline size_t strnistr(
416 _In_reads_or_z_opt_(count) const T1* str,
417 _In_ size_t count,
418 _In_z_ const T2* sample,
419 _In_ const std::locale& locale)
420 {
421 assert(str || !count);
422 assert(sample);
423 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
424 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
425 for (size_t offset = 0;; ++offset) {
426 for (size_t i = offset, j = 0;; ++i, ++j) {
427 if (!sample[j])
428 return offset;
429 if (i >= count || !str[i])
430 return npos;
431 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
432 break;
433 }
434 }
435 }
436
445 template <class T1, class T2>
446 inline size_t strcpy(
447 _Out_writes_z_(_String_length_(src) + 1) T1* dst,
448 _In_z_ const T2* src)
449 {
450 assert(dst && src);
451 for (size_t i = 0; ; ++i) {
452 if ((dst[i] = src[i]) == 0)
453 return i;
454 }
455 }
456
466 template <class T1, class T2>
467 inline size_t strncpy(
468 _Out_writes_(count) _Post_maybez_ T1* dst,
469 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
470 {
471 assert(dst && src || !count);
472 for (size_t i = 0; ; ++i) {
473 if (i >= count)
474 return i;
475 if ((dst[i] = src[i]) == 0)
476 return i;
477 }
478 }
479
490 template <class T1, class T2>
491 inline size_t strncpy(
492 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
493 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
494 {
495 assert(dst || !count_dst);
496 assert(src || !count_src);
497 for (size_t i = 0; ; ++i)
498 {
499 if (i > count_dst)
500 return i;
501 if (i > count_src) {
502 dst[i] = 0;
503 return i;
504 }
505 if ((dst[i] = src[i]) == 0)
506 return i;
507 }
508 }
509
518 template <class T1, class T2>
519 inline size_t strcat(
520 _In_z_ _Out_writes_z_(_String_length_(dst) + _String_length_(src) + 1) T1* dst,
521 _In_z_ const T2* src)
522 {
523 assert(dst && src);
524 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
525 if ((dst[j] = src[i]) == 0)
526 return j;
527 }
528 }
529
539 template <class T1, class T2>
540 inline size_t strncat(
541 _Out_writes_(count) _Post_maybez_ T1* dst,
542 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
543 {
544 assert(dst && src || !count);
545 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
546 if (i >= count)
547 return j;
548 if ((dst[j] = src[i]) == 0)
549 return j;
550 }
551 }
552
563 template <class T1, class T2>
564 inline size_t strncat(
565 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
566 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
567 {
568 assert(dst || !count_dst);
569 assert(src || !count_src);
570 for (size_t i = 0, j = stdex::strnlen<T1>(dst, count_dst); ; ++i, ++j)
571 {
572 if (j > count_dst)
573 return j;
574 if (i > count_src) {
575 dst[j] = 0;
576 return j;
577 }
578 if ((dst[j] = src[i]) == 0)
579 return j;
580 }
581 }
582
592 template <class T>
593 inline size_t crlf2nl(_Out_writes_z_(strlen(src)) T* dst, _In_z_ const T* src)
594 {
595 assert(dst);
596 assert(src);
597 size_t i, j;
598 for (i = j = 0; src[j];) {
599 if (src[j] != '\r' || src[j + 1] != '\n')
600 dst[i++] = src[j++];
601 else {
602 dst[i++] = '\n';
603 j += 2;
604 }
605 }
606 dst[i] = 0;
607 return i;
608 }
609
611 template <class T, class T_bin>
612 inline T_bin strtoint(
613 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
614 _Out_opt_ size_t* end,
615 _In_ int radix,
616 _Out_ uint8_t& flags)
617 {
618 assert(str || !count);
619 assert(radix == 0 || 2 <= radix && radix <= 36);
620
621 size_t i = 0;
622 T_bin value = 0, digit,
623 max_ui = (T_bin)-1,
624 max_ui_pre1, max_ui_pre2;
625
626 flags = 0;
627
628 // Skip leading spaces.
629 for (;; ++i) {
630 if (i >= count || !str[i]) goto error;
631 if (!isspace(str[i])) break;
632 }
633
634 // Read the sign.
635 if (str[i] == '+') {
636 flags &= ~0x01;
637 ++i;
638 if (i >= count || !str[i]) goto error;
639 }
640 else if (str[i] == '-') {
641 flags |= 0x01;
642 ++i;
643 if (i >= count || !str[i]) goto error;
644 }
645
646 if (radix == 16) {
647 // On hexadecimal, allow leading 0x.
648 if (str[i] == '0' && i + 1 < count && (str[i + 1] == 'x' || str[i + 1] == 'X')) {
649 i += 2;
650 if (i >= count || !str[i]) goto error;
651 }
652 }
653 else if (!radix) {
654 // Autodetect radix.
655 if (str[i] == '0') {
656 ++i;
657 if (i >= count || !str[i]) goto error;
658 if (str[i] == 'x' || str[i] == 'X') {
659 radix = 16;
660 ++i;
661 if (i >= count || !str[i]) goto error;
662 }
663 else
664 radix = 8;
665 }
666 else
667 radix = 10;
668 }
669
670 // We have the radix.
671 max_ui_pre1 = max_ui / (T_bin)radix;
672 max_ui_pre2 = max_ui % (T_bin)radix;
673 for (;;) {
674 if ('0' <= str[i] && str[i] <= '9')
675 digit = (T_bin)str[i] - '0';
676 else if ('A' <= str[i] && str[i] <= 'Z')
677 digit = (T_bin)str[i] - 'A' + '\x0a';
678 else if ('a' <= str[i] && str[i] <= 'z')
679 digit = (T_bin)str[i] - 'a' + '\x0a';
680 else
681 goto error;
682 if (digit >= (T_bin)radix)
683 goto error;
684
685 if (value < max_ui_pre1 || // Multiplication nor addition will not overflow.
686 value == max_ui_pre1 && digit <= max_ui_pre2) // Small digits will not overflow.
687 value = value * (T_bin)radix + digit;
688 else {
689 // Overflow!
690 flags |= 0x02;
691 }
692
693 ++i;
694 if (i >= count || !str[i])
695 goto error;
696 }
697
698 error:
699 if (end) *end = i;
700 return value;
701 }
703
714 template <class T, class T_bin>
715 T_bin strtoint(
716 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
717 _Out_opt_ size_t* end,
718 _In_ int radix)
719 {
720 uint8_t flags;
721 T_bin value;
722
723 switch (sizeof(T_bin)) {
724 case 1:
725 value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags);
726 if ((flags & 0x01) && (value & 0x80)) {
727 // Sign bit is 1 => overflow.
728 flags |= 0x02;
729 }
730 return (flags & 0x02) ?
731 (flags & 0x01) ? (T_bin)0x80 : (T_bin)0x7f :
732 (flags & 0x01) ? -value : value;
733
734 case 2:
735 value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags);
736 if ((flags & 0x01) && (value & 0x8000)) {
737 // Sign bit is 1 => overflow.
738 flags |= 0x02;
739 }
740 return (flags & 0x02) ?
741 (flags & 0x01) ? (T_bin)0x8000 : (T_bin)0x7fff :
742 (flags & 0x01) ? -value : value;
743
744 case 4:
745 value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags);
746 if ((flags & 0x01) && (value & 0x80000000)) {
747 // Sign bit is 1 => overflow.
748 flags |= 0x02;
749 }
750 return (flags & 0x02) ?
751 (flags & 0x01) ? (T_bin)0x80000000 : (T_bin)0x7fffffff :
752 (flags & 0x01) ? -value : value;
753
754 case 8:
755 value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags);
756 if ((flags & 0x01) && (value & 0x8000000000000000)) {
757 // Sign bit is 1 => overflow.
758 flags |= 0x02;
759 }
760 return (flags & 0x02) ?
761 (flags & 0x01) ? (T_bin)0x8000000000000000 : (T_bin)0x7fffffffffffffff :
762 (flags & 0x01) ? -value : value;
763
764 default:
765 throw std::invalid_argument("Unsupported bit length");
766 }
767 }
768
779 template <class T, class T_bin>
780 inline T_bin strtouint(
781 _In_reads_or_z_opt_(count) const T* str,
782 _In_ size_t count,
783 _Out_opt_ size_t* end,
784 _In_ int radix)
785 {
786 uint8_t flags;
787 T_bin value;
788
789 switch (sizeof(T_bin)) {
790 case 1: value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags); break;
791 case 2: value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags); break;
792 case 4: value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags); break;
793 case 8: value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags); break;
794 default: throw std::invalid_argument("Unsupported bit length");
795 }
796
797 return (flags & 0x02) ?
798 (flags & 0x01) ? (T_bin)0 : (T_bin)-1 :
799 (flags & 0x01) ? ~value : value;
800 }
801
812 template <class T>
813 inline int32_t strto32(
814 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
815 _Out_opt_ size_t* end,
816 _In_ int radix)
817 {
818 return strtoint<T, int32_t>(str, count, end, radix);
819 }
820
831 template <class T>
832 inline int64_t strto64(
833 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
834 _Out_opt_ size_t* end,
835 _In_ int radix)
836 {
837 return strtoint<T, int64_t>(str, count, end, radix);
838 }
839
851 template <class T>
852 inline intptr_t strtoi(
853 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
854 _Out_opt_ size_t* end,
855 _In_ int radix)
856 {
857#if defined(_WIN64) || defined(__LP64__)
858 return (intptr_t)strto64(str, count, end, radix);
859#else
860 return (intptr_t)strto32(str, count, end, radix);
861#endif
862 }
863
874 template <class T>
875 inline uint32_t strtou32(
876 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
877 _Out_opt_ size_t* end,
878 _In_ int radix)
879 {
880 return strtouint<T, uint32_t>(str, count, end, radix);
881 }
882
893 template <class T>
894 inline uint64_t strtou64(
895 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
896 _Out_opt_ size_t* end,
897 _In_ int radix)
898 {
899 return strtouint<T, uint64_t>(str, count, end, radix);
900 }
901
913 template <class T>
914 inline size_t strtoui(
915 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
916 _Out_opt_ size_t* end,
917 _In_ int radix)
918 {
919#if defined(_WIN64) || defined(__LP64__)
920 return (size_t)strtou64(str, count, end, radix);
921#else
922 return (size_t)strtou32(str, count, end, radix);
923#endif
924 }
925
927 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)
928 {
929 int r;
930#ifdef _WIN32
931 // Don't use _vsnprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
932#pragma warning(suppress: 4996)
933 r = _vsnprintf_l(str, capacity, format, locale, arg);
934#else
935 r = vsnprintf(str, capacity, format, arg);
936#endif
937 if (r == -1 && strnlen(str, capacity) == capacity) {
938 // Buffer overrun. Estimate buffer size for the next iteration.
939 capacity += std::max<size_t>(capacity / 8, 0x80);
940 if (capacity > INT_MAX)
941 throw std::invalid_argument("string too big");
942 return (int)capacity;
943 }
944 return r;
945 }
946
947 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)
948 {
949 int r;
950
951#ifdef _WIN32
952 // Don't use _vsnwprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
953#pragma warning(suppress: 4996)
954 r = _vsnwprintf_l(str, capacity, format, locale, arg);
955#else
956 r = vswprintf(str, capacity, format, arg);
957#endif
958 if (r == -1 && strnlen(str, capacity) == capacity) {
959 // Buffer overrun. Estimate buffer size for the next iteration.
960 capacity += std::max<size_t>(capacity / 8, 0x80);
961 if (capacity > INT_MAX)
962 throw std::invalid_argument("string too big");
963 return (int)capacity;
964 }
965 return r;
966 }
968
977 template<class _Elem, class _Traits, class _Ax>
978 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)
979 {
980 _Elem buf[1024/sizeof(_Elem)];
981
982 // Try with stack buffer first.
983 int count = vsnprintf(buf, _countof(buf) - 1, format, locale, arg);
984 if (count >= 0) {
985 // Copy from stack.
986 str.append(buf, count);
987 } else {
988 for (size_t capacity = 2*1024/sizeof(_Elem);; capacity *= 2) {
989 // Allocate on heap and retry.
990 auto buf_dyn = std::make_unique<_Elem[]>(capacity);
991 count = vsnprintf(buf_dyn.get(), capacity - 1, format, locale, arg);
992 if (count >= 0) {
993 str.append(buf_dyn.get(), count);
994 break;
995 }
996 }
997 }
998 }
999
1007 template<class _Elem, class _Traits, class _Ax>
1008 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, ...)
1009 {
1010 va_list arg;
1011 va_start(arg, locale);
1012 vappendf(str, format, locale, arg);
1013 va_end(arg);
1014 }
1015
1024 template<class _Elem, class _Traits, class _Ax>
1025 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)
1026 {
1027 str.clear();
1028 vappendf(str, format, locale, arg);
1029 }
1030
1038 template<class _Elem, class _Traits, class _Ax>
1039 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, ...)
1040 {
1041 va_list arg;
1042 va_start(arg, locale);
1043 vsprintf(str, format, locale, arg);
1044 va_end(arg);
1045 }
1046
1056 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1057 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)
1058 {
1059 std::basic_string<_Elem, _Traits, _Ax> str;
1060 vappendf(str, format, locale, arg);
1061 return str;
1062 }
1063
1072 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1073 inline std::basic_string<_Elem, _Traits, _Ax> sprintf(_In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, ...)
1074 {
1075 va_list arg;
1076 va_start(arg, locale);
1077 auto str = vsprintf(format, locale, arg);
1078 va_end(arg);
1079 return str;
1080 }
1081}