Skip to content

Commit

Permalink
Formatting thread::id, stacktrace_entry, and basic_stacktrace (#…
Browse files Browse the repository at this point in the history
…3861)

Co-authored-by: Stephan T. Lavavej <stl@microsoft.com>
  • Loading branch information
JMazurkiewicz and StephanTLavavej authored Aug 3, 2023
1 parent c879b26 commit ed8150e
Show file tree
Hide file tree
Showing 15 changed files with 1,028 additions and 70 deletions.
104 changes: 103 additions & 1 deletion stl/inc/format
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,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,108 @@ _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)] = {' '};
};

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 (!_STD in_range<int>(_Idx)) {
_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:
_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));
}

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 @@ -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 <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 @@ -13,6 +13,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 @@ -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<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& _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<polymorphic_allocator<stacktrace_entry>>;
}
Expand Down
45 changes: 44 additions & 1 deletion stl/inc/thread
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@
#include <__msvc_chrono.hpp>
#include <memory>
#include <process.h>
#include <string>
#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 @@ -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) {}

Expand Down Expand Up @@ -269,9 +281,40 @@ _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;
_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 <class _CharT>
struct formatter<thread::id, _CharT> {
private:
using _Pc = basic_format_parse_context<_CharT>;

public:
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(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<int>(_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<thread::id> {
using _ARGUMENT_TYPE_NAME _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS = thread::id;
Expand Down
Loading

0 comments on commit ed8150e

Please sign in to comment.