diff --git a/stl/inc/format b/stl/inc/format index 28804aa364..a4bac258ed 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -51,8 +51,8 @@ _EMIT_STL_WARNING(STL4038, "The contents of are available only with C++ #include #include #include -#include #include +#include #include #include #include @@ -3934,6 +3934,108 @@ _NODISCARD inline string _Unescape_braces(const _Add_newline _Add_nl, const stri return _Unescaped_str; } + +template +struct _Fill_align_and_width_specs { // used by thread::id and stacktrace_entry formatters + int _Width = -1; + int _Dynamic_width_index = -1; + _Fmt_align _Alignment = _Fmt_align::_None; + uint8_t _Fill_length = 1; + // At most one codepoint (so one char32_t or four utf-8 char8_t). + _CharT _Fill[4 / sizeof(_CharT)] = {' '}; +}; + +template +class _Fill_align_and_width_specs_setter { +public: + constexpr explicit _Fill_align_and_width_specs_setter( + _Fill_align_and_width_specs<_CharT>& _Specs_, basic_format_parse_context<_CharT>& _Parse_ctx_) + : _Specs(_Specs_), _Parse_ctx(_Parse_ctx_) {} + + constexpr void _On_align(const _Fmt_align _Aln) { + _Specs._Alignment = _Aln; + } + + constexpr void _On_fill(const basic_string_view<_CharT> _Sv) { + if (_Sv.size() > _STD size(_Specs._Fill)) { + _Throw_format_error("Invalid fill (too long)."); + } + + const auto _Pos = _STD _Copy_unchecked(_Sv._Unchecked_begin(), _Sv._Unchecked_end(), _Specs._Fill); + _STD fill(_Pos, _STD end(_Specs._Fill), _CharT{}); + _Specs._Fill_length = static_cast(_Sv.size()); + } + + constexpr void _On_width(const int _Width) { + _Specs._Width = _Width; + } + + constexpr void _On_dynamic_width(const size_t _Arg_id) { + _Parse_ctx.check_arg_id(_Arg_id); + _Specs._Dynamic_width_index = _Verify_dynamic_arg_index_in_range(_Arg_id); + } + + constexpr void _On_dynamic_width(const _Auto_id_tag) { + _Specs._Dynamic_width_index = _Verify_dynamic_arg_index_in_range(_Parse_ctx.next_arg_id()); + } + +private: + _Fill_align_and_width_specs<_CharT>& _Specs; + basic_format_parse_context<_CharT>& _Parse_ctx; + + _NODISCARD static constexpr int _Verify_dynamic_arg_index_in_range(const size_t _Idx) { + if (!_STD in_range(_Idx)) { + _Throw_format_error("Dynamic width index is too large."); + } + + return static_cast(_Idx); + } +}; + +template +_NODISCARD constexpr const _CharT* _Parse_fill_align_and_width_specs( + const _CharT* _Begin, const _CharT* _End, _Callbacks_type&& _Callbacks) { + if (_Begin == _End || *_Begin == '}') { + return _Begin; + } + + _Begin = _Parse_align(_Begin, _End, _Callbacks); + if (_Begin == _End) { + return _Begin; + } + + return _Parse_width(_Begin, _End, _Callbacks); +} + +template +struct _Fill_align_and_width_formatter { +public: + _NODISCARD constexpr auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { + _Fill_align_and_width_specs_setter<_CharT> _Callback{_Specs, _Parse_ctx}; + const auto _It = + _Parse_fill_align_and_width_specs(_Parse_ctx._Unchecked_begin(), _Parse_ctx._Unchecked_end(), _Callback); + if (_It != _Parse_ctx._Unchecked_end() && *_It != '}') { + _Throw_format_error("Missing '}' in format string."); + } + + return _Parse_ctx.begin() + (_It - _Parse_ctx._Unchecked_begin()); + } + + template + _NODISCARD constexpr auto _Format( + _FormatContext& _Format_ctx, const int _Width, _Fmt_align _Default_align, _Func&& _Fn) const { + _Fill_align_and_width_specs _Format_specs = _Specs; + if (_Specs._Dynamic_width_index >= 0) { + _Format_specs._Width = + _Get_dynamic_specs<_Width_checker>(_Format_ctx.arg(static_cast(_Specs._Dynamic_width_index))); + } + + return _Write_aligned(_Format_ctx.out(), _Width, _Format_specs, _Default_align, _STD forward<_Func>(_Fn)); + } + +private: + _Fill_align_and_width_specs<_CharT> _Specs; +}; #endif // _HAS_CXX23 #undef _FMT_P2286_END diff --git a/stl/inc/mutex b/stl/inc/mutex index c6387b6fd9..b133a8c1ce 100644 --- a/stl/inc/mutex +++ b/stl/inc/mutex @@ -532,74 +532,6 @@ public: }; #endif // _HAS_CXX17 -#if defined(_M_CEE) || defined(_M_ARM64EC) || defined(_M_HYBRID) \ - || defined(__clang__) // TRANSITION, Clang doesn't recognize /ALTERNATENAME, not yet reported -#define _WINDOWS_API __stdcall -#define _RENAME_WINDOWS_API(_Api) _Api##_clr -#else // ^^^ use forwarders / use /ALTERNATENAME vvv -#define _WINDOWS_API __declspec(dllimport) __stdcall -#define _RENAME_WINDOWS_API(_Api) _Api -#endif // ^^^ use /ALTERNATENAME ^^^ - -// WINBASEAPI -// BOOL -// WINAPI -// InitOnceBeginInitialize( -// _Inout_ LPINIT_ONCE lpInitOnce, -// _In_ DWORD dwFlags, -// _Out_ PBOOL fPending, -// _Outptr_opt_result_maybenull_ LPVOID* lpContext -// ); -extern "C" _NODISCARD int _WINDOWS_API _RENAME_WINDOWS_API(__std_init_once_begin_initialize)( - void** _LpInitOnce, unsigned long _DwFlags, int* _FPending, void** _LpContext) noexcept; - -// WINBASEAPI -// BOOL -// WINAPI -// InitOnceComplete( -// _Inout_ LPINIT_ONCE lpInitOnce, -// _In_ DWORD dwFlags, -// _In_opt_ LPVOID lpContext -// ); -extern "C" _NODISCARD int _WINDOWS_API _RENAME_WINDOWS_API(__std_init_once_complete)( - void** _LpInitOnce, unsigned long _DwFlags, void* _LpContext) noexcept; - -extern "C" [[noreturn]] void __stdcall __std_init_once_link_alternate_names_and_abort() noexcept; - -// #define RTL_RUN_ONCE_INIT_FAILED 0x00000004UL -// #define INIT_ONCE_INIT_FAILED RTL_RUN_ONCE_INIT_FAILED -_INLINE_VAR constexpr unsigned long _Init_once_init_failed = 0x4UL; - -struct _Init_once_completer { - once_flag& _Once; - unsigned long _DwFlags; - ~_Init_once_completer() { - if (!_RENAME_WINDOWS_API(__std_init_once_complete)(&_Once._Opaque, _DwFlags, nullptr)) { - __std_init_once_link_alternate_names_and_abort(); - } - } -}; - -_EXPORT_STD template -void(call_once)(once_flag& _Once, _Fn&& _Fx, _Args&&... _Ax) noexcept( - noexcept(_STD invoke(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...))) /* strengthened */ { - // call _Fx(_Ax...) once - // parentheses against common "#define call_once(flag,func) pthread_once(flag,func)" - int _Pending; - if (!_RENAME_WINDOWS_API(__std_init_once_begin_initialize)(&_Once._Opaque, 0, &_Pending, nullptr)) { - _CSTD abort(); - } - - if (_Pending != 0) { - _Init_once_completer _Op{_Once, _Init_once_init_failed}; - _STD invoke(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...); - _Op._DwFlags = 0; - } -} - -#undef _WINDOWS_API -#undef _RENAME_WINDOWS_API - _EXPORT_STD enum class cv_status { // names for wait returns no_timeout, timeout diff --git a/stl/inc/stacktrace b/stl/inc/stacktrace index 5d3dfe6b9d..43182d0ddf 100644 --- a/stl/inc/stacktrace +++ b/stl/inc/stacktrace @@ -13,6 +13,7 @@ _EMIT_STL_WARNING(STL4038, "The contents of are available only with #else // ^^^ !_HAS_CXX23 / _HAS_CXX23 vvv #include +#include #include #include #include @@ -348,6 +349,42 @@ ostream& operator<<(ostream& _Os, const basic_stacktrace<_Alloc>& _St) { return _Os << _STD to_string(_St); } +#ifdef __cpp_lib_concepts +template <> +struct formatter { + constexpr format_parse_context::iterator parse(format_parse_context& _Parse_ctx) { + return _Impl.parse(_Parse_ctx); + } + + template + _FormatContext::iterator format(const stacktrace_entry& _Val, _FormatContext& _Format_ctx) const { + const auto _Str = _STD to_string(_Val); + return _Impl._Format(_Format_ctx, static_cast(_Str.size()), _Fmt_align::_Left, + [&](_FormatContext::iterator _Out) { return _RANGES copy(_Str, _STD move(_Out)).out; }); + } + +private: + _Fill_align_and_width_formatter _Impl; +}; + +template +struct formatter> { + constexpr format_parse_context::iterator parse(format_parse_context& _Parse_ctx) { + const auto _First = _Parse_ctx.begin(); + if (_First != _Parse_ctx.end() && *_First != '}') { + _Throw_format_error("For formatter>, format-spec must be empty."); + } + + return _First; + } + + template + _FormatContext::iterator format(const basic_stacktrace<_Alloc>& _Val, _FormatContext& _Format_ctx) const { + return _RANGES copy(_STD to_string(_Val), _Format_ctx.out()).out; + } +}; +#endif // __cpp_lib_concepts + namespace pmr { _EXPORT_STD using stacktrace = basic_stacktrace>; } diff --git a/stl/inc/thread b/stl/inc/thread index 41a5f4aeb0..932cc1cf4d 100644 --- a/stl/inc/thread +++ b/stl/inc/thread @@ -10,13 +10,19 @@ #include <__msvc_chrono.hpp> #include #include +#include #include #include + #if _HAS_CXX20 #include #include #endif // _HAS_CXX20 +#if _HAS_CXX23 +#include +#endif // _HAS_CXX23 + #ifdef _M_CEE_PURE #error is not supported when compiling with /clr:pure. #endif // _M_CEE_PURE @@ -207,6 +213,12 @@ class thread::id { // thread id public: id() noexcept = default; // id for no thread +#if _HAS_CXX23 + _NODISCARD _Thrd_id_t _Get_underlying_id() const noexcept { + return _Id; + } +#endif // _HAS_CXX23 + private: explicit id(_Thrd_id_t _Other_id) noexcept : _Id(_Other_id) {} @@ -269,9 +281,40 @@ _NODISCARD inline bool operator>=(thread::id _Left, thread::id _Right) noexcept _EXPORT_STD template basic_ostream<_Ch, _Tr>& operator<<(basic_ostream<_Ch, _Tr>& _Str, thread::id _Id) { - return _Str << _Id._Id; + _STL_INTERNAL_STATIC_ASSERT(sizeof(_Thrd_id_t) == 4); + _Ch _Buff[11]; // can hold 2^32 - 1, plus terminating null + _Ch* _RNext = _STD end(_Buff); + *--_RNext = static_cast<_Ch>('\0'); + _RNext = _STD _UIntegral_to_buff(_RNext, _Id._Id); + return _Str << _RNext; } +#if _HAS_CXX23 && defined(__cpp_lib_concepts) +template +struct formatter { +private: + using _Pc = basic_format_parse_context<_CharT>; + +public: + constexpr _Pc::iterator parse(_Pc& _Parse_ctx) { + return _Impl.parse(_Parse_ctx); + } + + template + _FormatContext::iterator format(thread::id _Val, _FormatContext& _Format_ctx) const { + _STL_INTERNAL_STATIC_ASSERT(sizeof(_Thrd_id_t) == 4); + _CharT _Buff[10]; // can hold 2^32 - 1 + _CharT* const _Last = _STD end(_Buff); + const _CharT* const _First = _STD _UIntegral_to_buff(_Last, _Val._Get_underlying_id()); + return _Impl._Format(_Format_ctx, static_cast(_Last - _First), _Fmt_align::_Right, + [&](_FormatContext::iterator _Out) { return _RANGES copy(_First, _Last, _STD move(_Out)).out; }); + } + +private: + _Fill_align_and_width_formatter<_CharT> _Impl; +}; +#endif // _HAS_CXX23 && defined(__cpp_lib_concepts) + template <> struct hash { using _ARGUMENT_TYPE_NAME _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS = thread::id; diff --git a/stl/inc/xcall_once.h b/stl/inc/xcall_once.h index 00d95937b8..8ea01a7d65 100644 --- a/stl/inc/xcall_once.h +++ b/stl/inc/xcall_once.h @@ -8,6 +8,9 @@ #include #if _STL_COMPILER_PREPROCESSOR +#include +#include + #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) @@ -43,6 +46,74 @@ union _Immortalizer_impl { // constructs _Ty, never destroys _Ty _Storage; }; + +#if defined(_M_CEE) || defined(_M_ARM64EC) || defined(_M_HYBRID) \ + || defined(__clang__) // TRANSITION, Clang doesn't recognize /ALTERNATENAME, not yet reported +#define _WINDOWS_API __stdcall +#define _RENAME_WINDOWS_API(_Api) _Api##_clr +#else // ^^^ use forwarders / use /ALTERNATENAME vvv +#define _WINDOWS_API __declspec(dllimport) __stdcall +#define _RENAME_WINDOWS_API(_Api) _Api +#endif // ^^^ use /ALTERNATENAME ^^^ + +// WINBASEAPI +// BOOL +// WINAPI +// InitOnceBeginInitialize( +// _Inout_ LPINIT_ONCE lpInitOnce, +// _In_ DWORD dwFlags, +// _Out_ PBOOL fPending, +// _Outptr_opt_result_maybenull_ LPVOID* lpContext +// ); +extern "C" _NODISCARD int _WINDOWS_API _RENAME_WINDOWS_API(__std_init_once_begin_initialize)( + void** _LpInitOnce, unsigned long _DwFlags, int* _FPending, void** _LpContext) noexcept; + +// WINBASEAPI +// BOOL +// WINAPI +// InitOnceComplete( +// _Inout_ LPINIT_ONCE lpInitOnce, +// _In_ DWORD dwFlags, +// _In_opt_ LPVOID lpContext +// ); +extern "C" _NODISCARD int _WINDOWS_API _RENAME_WINDOWS_API(__std_init_once_complete)( + void** _LpInitOnce, unsigned long _DwFlags, void* _LpContext) noexcept; + +extern "C" [[noreturn]] void __stdcall __std_init_once_link_alternate_names_and_abort() noexcept; + +// #define RTL_RUN_ONCE_INIT_FAILED 0x00000004UL +// #define INIT_ONCE_INIT_FAILED RTL_RUN_ONCE_INIT_FAILED +_INLINE_VAR constexpr unsigned long _Init_once_init_failed = 0x4UL; + +struct _Init_once_completer { + once_flag& _Once; + unsigned long _DwFlags; + ~_Init_once_completer() { + if (!_RENAME_WINDOWS_API(__std_init_once_complete)(&_Once._Opaque, _DwFlags, nullptr)) { + __std_init_once_link_alternate_names_and_abort(); + } + } +}; + +_EXPORT_STD template +void(call_once)(once_flag& _Once, _Fn&& _Fx, _Args&&... _Ax) noexcept( + noexcept(_STD invoke(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...))) /* strengthened */ { + // call _Fx(_Ax...) once + // parentheses against common "#define call_once(flag,func) pthread_once(flag,func)" + int _Pending; + if (!_RENAME_WINDOWS_API(__std_init_once_begin_initialize)(&_Once._Opaque, 0, &_Pending, nullptr)) { + _CSTD abort(); + } + + if (_Pending != 0) { + _Init_once_completer _Op{_Once, _Init_once_init_failed}; + _STD invoke(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...); + _Op._DwFlags = 0; + } +} + +#undef _WINDOWS_API +#undef _RENAME_WINDOWS_API _STD_END #pragma pop_macro("new") diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index ac987d46a9..e4ec910912 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -374,6 +374,7 @@ // P2540R1 Empty Product For Certain Views // P2549R1 unexpected::error() // P2652R2 Disallowing User Specialization Of allocator_traits +// P2693R1 Formatting thread::id And stacktrace // P2713R1 Escaping Improvements In std::format // _HAS_CXX23 and _SILENCE_ALL_CXX23_DEPRECATION_WARNINGS control: @@ -1783,6 +1784,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #ifdef __cpp_lib_concepts #define __cpp_lib_containers_ranges 202202L #define __cpp_lib_expected 202211L +#define __cpp_lib_formatters 202302L #endif // __cpp_lib_concepts #define __cpp_lib_forward_like 202207L diff --git a/tests/std/include/test_format_support.hpp b/tests/std/include/test_format_support.hpp index 77cc36eb85..969f6f57bb 100644 --- a/tests/std/include/test_format_support.hpp +++ b/tests/std/include/test_format_support.hpp @@ -4,9 +4,13 @@ #pragma once #include +#include #include #include +#include +#include #include +#include #include // copied from the string_view tests @@ -18,6 +22,10 @@ struct choose_literal { static constexpr const char* choose(const char* s, const wchar_t*) { return s; } + + static constexpr char choose(char c, wchar_t) { + return c; + } }; template <> @@ -25,6 +33,10 @@ struct choose_literal { static constexpr const wchar_t* choose(const char*, const wchar_t* s) { return s; } + + static constexpr wchar_t choose(char, wchar_t c) { + return c; + } }; #define TYPED_LITERAL(CharT, Literal) (choose_literal::choose(Literal, L##Literal)) @@ -126,3 +138,90 @@ void test_parse_helper(const CharT* (*func)(const CharT*, const CharT*, callback assert(err_expected); } } + +template +struct FormatFn { + template + [[nodiscard]] auto operator()( + const std::basic_format_string...> str, Args&&... args) const { + return std::format(str, std::forward(args)...); + } +}; + +template +struct VFormatFn { + template + [[nodiscard]] auto operator()(const std::basic_string_view str, Args&&... args) const { + if constexpr (std::same_as) { + return std::vformat(str, std::make_format_args(std::forward(args)...)); + } else { + return std::vformat(str, std::make_wformat_args(std::forward(args)...)); + } + } +}; + +template +struct ExpectFormatError { +private: + VFormatFn base; + +public: + template + void operator()(const std::basic_string_view str, Args&&... args) const { + try { + (void) base(str, std::forward(args)...); + assert(false && "No exception."); + } catch (const std::format_error&) { + return; + } catch (...) { + assert(false && "Incorrect exception."); + } + } +}; + +template +struct MoveOnlyFormat { +private: + struct StringInserter { + using iterator_category = std::output_iterator_tag; + using difference_type = std::ptrdiff_t; + using container_type = std::basic_string; + + StringInserter() = default; + StringInserter(StringInserter&&) = default; + StringInserter& operator=(StringInserter&&) = default; + + StringInserter(const StringInserter&) = delete; + StringInserter& operator=(const StringInserter&) = delete; + + StringInserter& operator=(const CharT val) { + str.push_back(val); + return *this; + } + + StringInserter& operator*() { + return *this; + } + + StringInserter& operator++() { + return *this; + } + + StringInserter operator++(int) { + return *this; + } + + std::basic_string str; + }; + +public: + static_assert(std::output_iterator); + static_assert(!std::copyable); + static_assert(std::movable); + + template + [[nodiscard]] auto operator()( + const std::basic_format_string...> str, Args&&... args) const { + return std::format_to(StringInserter{}, str, std::forward(args)...).str; + } +}; diff --git a/tests/std/test.lst b/tests/std/test.lst index 111091a088..c2190304dc 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -625,6 +625,9 @@ tests\P2505R5_monadic_functions_for_std_expected tests\P2517R1_apply_conditional_noexcept tests\P2538R1_adl_proof_std_projected tests\P2609R3_relaxing_ranges_just_a_smidge +tests\P2693R1_ostream_and_thread_id +tests\P2693R1_text_formatting_stacktrace +tests\P2693R1_text_formatting_thread_id tests\VSO_0000000_allocator_propagation tests\VSO_0000000_any_calling_conventions tests\VSO_0000000_c_math_functions diff --git a/tests/std/tests/P2693R1_ostream_and_thread_id/env.lst b/tests/std/tests/P2693R1_ostream_and_thread_id/env.lst new file mode 100644 index 0000000000..f141421b29 --- /dev/null +++ b/tests/std/tests/P2693R1_ostream_and_thread_id/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\impure_matrix.lst diff --git a/tests/std/tests/P2693R1_ostream_and_thread_id/test.cpp b/tests/std/tests/P2693R1_ostream_and_thread_id/test.cpp new file mode 100644 index 0000000000..aca73e6d44 --- /dev/null +++ b/tests/std/tests/P2693R1_ostream_and_thread_id/test.cpp @@ -0,0 +1,128 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +// copied from the 'test_format_support.hpp' header +template +struct choose_literal; // not defined + +template <> +struct choose_literal { + static constexpr const char* choose(const char* s, const wchar_t*) { + return s; + } +}; + +template <> +struct choose_literal { + static constexpr const wchar_t* choose(const char*, const wchar_t* s) { + return s; + } +}; + +#define STR(Literal) (choose_literal::choose(Literal, L##Literal)) + +template +void check_fmtflags_and_locale(thread::id id) { + // changing fmtflags other than fill or align should not affect text representation of thread::id + auto make_text_rep = [id](ios_base::fmtflags flags = {}, ios_base::fmtflags mask = {}) { + basic_ostringstream ss; + ss.setf(flags, mask); + ss << id; + return ss.str(); + }; + + // changing locale should not affect text representation of thread::id + auto make_localized_text_rep = [id](const char* name) { + basic_ostringstream ss; + try { + ss.imbue(locale{name}); + } catch (...) { + } + ss << id; + return ss.str(); + }; + + // changing precision should not affect text representation of thread::id + auto make_text_rep_with_precision = [id](int prec) { + basic_ostringstream ss; + ss.precision(prec); + ss << id; + return ss.str(); + }; + + const array, 14> reps = { + make_text_rep(), + make_text_rep(ios_base::fixed, ios_base::basefield), + make_text_rep(ios_base::hex | ios_base::uppercase, ios_base::basefield), + make_text_rep(ios_base::hex, ios_base::basefield), + make_text_rep(ios_base::oct | ios_base::showbase, ios_base::basefield), + make_text_rep(ios_base::oct, ios_base::basefield), + make_text_rep(ios_base::showpos, ios_base::basefield), + make_localized_text_rep(""), + make_localized_text_rep("de-DE"), + make_localized_text_rep("en-US"), + make_localized_text_rep("pl-PL"), + make_text_rep_with_precision(1), + make_text_rep_with_precision(6), + make_text_rep_with_precision(32), + }; + + // all text representations should be the same + assert(adjacent_find(reps.begin(), reps.end(), not_equal_to<>{}) == reps.end()); +} + +template +void check_fill_and_align() { + thread::id id; + + { // Align left + basic_ostringstream ss; + ss.setf(ios_base::left, ios_base::adjustfield); + ss.fill('*'); + ss.width(5); + ss << id; + assert(ss.str() == STR("0****")); + } + + { // Align right + basic_ostringstream ss; + ss.setf(ios_base::right, ios_base::adjustfield); + ss.fill(':'); + ss.width(10); + ss << id; + assert(ss.str() == STR(":::::::::0")); + } + + { // Align internal + basic_ostringstream ss; + ss.setf(ios_base::internal, ios_base::adjustfield); + ss.fill('_'); + ss.width(3); + ss << id; + assert(ss.str() == STR("__0")); + } +} + +template +void test() { + check_fmtflags_and_locale(thread::id{}); + check_fmtflags_and_locale(this_thread::get_id()); + check_fill_and_align(); +} + +int main() { + test(); + test(); +} diff --git a/tests/std/tests/P2693R1_text_formatting_stacktrace/env.lst b/tests/std/tests/P2693R1_text_formatting_stacktrace/env.lst new file mode 100644 index 0000000000..d7beb961d2 --- /dev/null +++ b/tests/std/tests/P2693R1_text_formatting_stacktrace/env.lst @@ -0,0 +1,8 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst +RUNALL_CROSSLIST +PM_CL="/Zi /DHAS_DEBUG_INFO" PM_LINK="/debug" +PM_CL="/DHAS_EXPORT" +PM_CL="" diff --git a/tests/std/tests/P2693R1_text_formatting_stacktrace/test.cpp b/tests/std/tests/P2693R1_text_formatting_stacktrace/test.cpp new file mode 100644 index 0000000000..7186f55df5 --- /dev/null +++ b/tests/std/tests/P2693R1_text_formatting_stacktrace/test.cpp @@ -0,0 +1,232 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_format_support.hpp" + +using namespace std; + +template