Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Formatting thread::id, stacktrace_entry, and basic_stacktrace #3861

Merged
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e387949
Move `std::call_once` to `xcall_once.h` and remove `<mutex>` include …
JMazurkiewicz Jul 11, 2023
4e1e84c
Testing: implement `choose_literal` for character types
JMazurkiewicz Jul 11, 2023
09559db
Testing: test `thread::id` formatter
JMazurkiewicz Jul 11, 2023
7aa7a9d
Add new `_Fill_align_and_width_specs` for `thread::id` and `stacktrac…
JMazurkiewicz Jul 11, 2023
160da41
Implement `thread::id` formatter
JMazurkiewicz Jul 11, 2023
d55eb21
Fix `operator<<(thread::id)`, see LLVM-62073
JMazurkiewicz Jul 11, 2023
0ae81d9
Testing: fix calls to `setf` in `thread::id` tests
JMazurkiewicz Jul 11, 2023
9c97071
Testing: test fixed `operator<<(thread::id)`
JMazurkiewicz Jul 11, 2023
15e9f33
Testing: move format adaptors to the `"test_format_support.hpp"` head…
JMazurkiewicz Jul 11, 2023
c01bebf
Testing: test `stacktrace_entry` and `basic_stacktrace` formatters
JMazurkiewicz Jul 11, 2023
b161a2f
Implement `stacktrace_entry` and `basic_stacktrace` formatters
JMazurkiewicz Jul 11, 2023
1ac191c
Add feature test macro
JMazurkiewicz Jul 11, 2023
c2e90bf
Improve test coverage
JMazurkiewicz Jul 11, 2023
c3f5f30
`<format>`: `protected` -> `private`
JMazurkiewicz Jul 11, 2023
f0a377a
`operator<<(thread::id)` now uses `_UIntegral_to_buff` from `<string>`
JMazurkiewicz Jul 12, 2023
4e06a87
Don't use `<charconv>`
JMazurkiewicz Jul 13, 2023
db25408
Merge branch 'main' into format/stacktrace-thread-id
StephanTLavavej Jul 19, 2023
a0f8988
Code review feedback.
StephanTLavavej Jul 19, 2023
3c85a46
Merge branch 'main' into format/stacktrace-thread-id
StephanTLavavej Jul 27, 2023
7e4d845
Catch test exceptions.
StephanTLavavej Jul 27, 2023
f503c64
You want me to give him THE CLAMPS, boss?
StephanTLavavej Aug 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 105 additions & 1 deletion stl/inc/format
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ _EMIT_STL_WARNING(STL4038, "The contents of <format> are available only with C++
#include <iterator>
#include <limits>
#include <locale>
#include <mutex>
#include <stdexcept>
#include <xcall_once.h>
#include <xfilesystem_abi.h>
#include <xstring>
#include <xutility>
Expand Down Expand Up @@ -3934,6 +3934,110 @@ _NODISCARD inline string _Unescape_braces(const _Add_newline _Add_nl, const stri

return _Unescaped_str;
}

template <class _CharT>
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)] = {' '};
};
Comment on lines +3939 to +3946
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect this could be unified with _Basic_format_specs and _Dynamic_format_specs, I need to work out what that would actually look like though, and it probably requires nontrivial inheritance.


template <class _CharT>
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<uint8_t>(_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 (_Idx > static_cast<size_t>((numeric_limits<int>::max)())) {
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
_Throw_format_error("Dynamic width index is too large.");
}

return static_cast<int>(_Idx);
}
};

template <class _CharT, class _Callbacks_type>
_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 <class _CharT>
struct _Fill_align_and_width_formatter {
public:
_Fill_align_and_width_formatter() = default;
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved

_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 <class _FormatContext, class _Func>
_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<size_t>(_Specs._Dynamic_width_index)));
}

return _Write_aligned(_Format_ctx.out(), _Width, _Format_specs, _Default_align, std::forward<_Func>(_Fn));
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
}

private:
_Fill_align_and_width_specs<_CharT> _Specs;
};
#endif // _HAS_CXX23

#undef _FMT_P2286_END
Expand Down
68 changes: 0 additions & 68 deletions stl/inc/mutex
Original file line number Diff line number Diff line change
Expand Up @@ -557,74 +557,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 <class _Fn, class... _Args>
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
Expand Down
37 changes: 37 additions & 0 deletions stl/inc/stacktrace
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ _EMIT_STL_WARNING(STL4038, "The contents of <stacktrace> are available only with
#else // ^^^ !_HAS_CXX23 / _HAS_CXX23 vvv

#include <cstdint>
#include <format>
#include <string>
#include <type_traits>
#include <vector>
Expand Down Expand Up @@ -349,6 +350,42 @@ ostream& operator<<(ostream& _Os, const basic_stacktrace<_Alloc>& _St) {
return _Os << _STD to_string(_St);
}

#ifdef __cpp_lib_concepts
template <>
struct formatter<stacktrace_entry> {
constexpr format_parse_context::iterator parse(format_parse_context& _Parse_ctx) {
return _Impl.parse(_Parse_ctx);
}

template <class _FormatContext>
_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<int>(_Str.size()), _Fmt_align::_Left,
[&](_FormatContext::iterator _Out) { return _RANGES copy(_Str, _STD move(_Out)).out; });
}

private:
_Fill_align_and_width_formatter<char> _Impl;
};

template <class _Alloc>
struct formatter<basic_stacktrace<_Alloc>> {
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<basic_stacktrace<Allocator>>, format-spec must be empty.");
}

return _First;
}

template <class _FormatContext>
_FormatContext::iterator format(const basic_stacktrace<_Alloc>& _Val, _FormatContext& _FormatCtx) const {
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
return _RANGES copy(_STD to_string(_Val), _FormatCtx.out()).out;
}
};
#endif // __cpp_lib_concepts

namespace pmr {
_EXPORT_STD using stacktrace = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>;
}
Expand Down
43 changes: 42 additions & 1 deletion stl/inc/thread
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,19 @@
#include <__msvc_chrono.hpp>
#include <memory>
#include <process.h>
#include <sstream>
#include <tuple>
#include <xthreads.h>

#if _HAS_CXX20
#include <compare>
#include <stop_token>
#endif // _HAS_CXX20

#if _HAS_CXX23
#include <format>
#endif // _HAS_CXX23

#ifdef _M_CEE_PURE
#error <thread> is not supported when compiling with /clr:pure.
#endif // _M_CEE_PURE
Expand Down Expand Up @@ -208,6 +214,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
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved

private:
explicit id(_Thrd_id_t _Other_id) noexcept : _Id(_Other_id) {}

Expand Down Expand Up @@ -270,9 +282,38 @@ _NODISCARD inline bool operator>=(thread::id _Left, thread::id _Right) noexcept

_EXPORT_STD template <class _Ch, class _Tr>
basic_ostream<_Ch, _Tr>& operator<<(basic_ostream<_Ch, _Tr>& _Str, thread::id _Id) {
return _Str << _Id._Id;
basic_ostringstream<_Ch, _Tr> _Out;
_Out.imbue(locale::classic());
_Out << _Id._Id;
return _Str << _Out.str();
JMazurkiewicz marked this conversation as resolved.
Show resolved Hide resolved
}

#if _HAS_CXX23 && defined(__cpp_lib_concepts)
template <class _CharT>
struct formatter<thread::id, _CharT> {
public:
using _Pc = basic_format_parse_context<_CharT>;
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved

constexpr _Pc::iterator parse(_Pc& _Parse_ctx) {
return _Impl.parse(_Parse_ctx);
}

template <class _FormatContext>
_FormatContext::iterator format(thread::id _Val, _FormatContext& _Format_ctx) const {
_STL_INTERNAL_STATIC_ASSERT(is_same_v<_Thrd_id_t, unsigned int>);
char _Buffer[10]; // size(string with decimal value of numeric_limits<_Thrd_id_t>::max()) == 10
const auto [_End, _Ec] = _STD to_chars(_Buffer, end(_Buffer), _Val._Get_underlying_id());
_STL_INTERNAL_CHECK(_Ec == errc{});

return _Impl._Format(_Format_ctx, static_cast<int>(_End - _Buffer), _Fmt_align::_Right,
[&](_FormatContext::iterator _Out) { return _Widen_and_copy<_CharT>(_Buffer, _End, _STD move(_Out)); });
}

private:
_Fill_align_and_width_formatter<_CharT> _Impl;
};
#endif // _HAS_CXX23 && defined(__cpp_lib_concepts)

template <>
struct hash<thread::id> {
using _ARGUMENT_TYPE_NAME _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS = thread::id;
Expand Down
Loading