From 915b275bd5606bebf11a2efac67599c5c1f0cc0b Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Sun, 7 Jul 2024 23:46:49 +0200 Subject: [PATCH 01/58] Add enable_nonlocking_formatter_optimization customization point and specialize for standard types. --- stl/inc/__msvc_formatter.hpp | 43 ++++++++++++++++++++++++++++++++++++ stl/inc/chrono | 10 +++++++++ 2 files changed, 53 insertions(+) diff --git a/stl/inc/__msvc_formatter.hpp b/stl/inc/__msvc_formatter.hpp index 5ff1bea2f4..7c477a4385 100644 --- a/stl/inc/__msvc_formatter.hpp +++ b/stl/inc/__msvc_formatter.hpp @@ -90,6 +90,9 @@ enum class _Basic_format_arg_type : uint8_t { static_assert(static_cast(_Basic_format_arg_type::_Custom_type) < 16, "must fit in 4-bit bitfield"); #if _HAS_CXX23 +_EXPORT_STD template +constexpr bool enable_nonlocking_formatter_optimization = false; + _NODISCARD consteval bool _Is_debug_enabled_fmt_type(_Basic_format_arg_type _Ty) { return _Ty == _Basic_format_arg_type::_Char_type || _Ty == _Basic_format_arg_type::_CString_type || _Ty == _Basic_format_arg_type::_String_type; @@ -170,7 +173,16 @@ struct _Formatter_base { }; _FMT_P2286_END +#if _HAS_CXX23 +#define _FORMAT_SPECIALIZE_NON_LOCKING_FOR(_Type) \ + template <> \ + inline constexpr bool enable_nonlocking_formatter_optimization<_Type> = true; +#else // ^^^ _HAS_CXX23 / !_HAS_CXX23 vvv +#define _FORMAT_SPECIALIZE_NON_LOCKING_FOR(_Type) +#endif // ^^^ !_HAS_CXX23 ^^^ + #define _FORMAT_SPECIALIZE_FOR(_Type, _ArgType) \ + _FORMAT_SPECIALIZE_NON_LOCKING_FOR(_Type); \ template <_Format_supported_charT _CharT> \ struct formatter<_Type, _CharT> : _Formatter_base<_Type, _CharT, _ArgType> {} @@ -193,6 +205,7 @@ _FORMAT_SPECIALIZE_FOR(signed char, _Basic_format_arg_type::_Int_type); _FORMAT_SPECIALIZE_FOR(unsigned char, _Basic_format_arg_type::_UInt_type); #undef _FORMAT_SPECIALIZE_FOR +#undef _FORMAT_SPECIALIZE_NON_LOCKING_FOR // not using the macro because we'd like to add 'set_debug_format' member function in C++23 mode template <_Format_supported_charT _CharT> @@ -361,6 +374,36 @@ struct formatter, _CharT>; template <_Format_supported_charT _CharT, class... _Types> struct formatter, _CharT>; + +template <_Format_supported_charT _CharT> +inline constexpr bool enable_nonlocking_formatter_optimization<_CharT> = true; + +template <_Format_supported_charT _CharT> +inline constexpr bool enable_nonlocking_formatter_optimization<_CharT*> = true; + +template <_Format_supported_charT _CharT> +inline constexpr bool enable_nonlocking_formatter_optimization = true; + +template <_Format_supported_charT _CharT, size_t _Nx> +inline constexpr bool enable_nonlocking_formatter_optimization<_CharT[_Nx]> = true; + +template <_Format_supported_charT _CharT, class _Traits, class _Allocator> +inline constexpr bool enable_nonlocking_formatter_optimization> = true; + +template <_Format_supported_charT _CharT, class _Traits> +inline constexpr bool enable_nonlocking_formatter_optimization> = true; + +template +inline constexpr bool enable_nonlocking_formatter_optimization> = + _STD enable_nonlocking_formatter_optimization<_Ty1> && _STD enable_nonlocking_formatter_optimization<_Ty2>; + +template +inline constexpr bool enable_nonlocking_formatter_optimization> = + (_STD enable_nonlocking_formatter_optimization<_Ts> && ...); + +template <_RANGES input_range _Rng> + requires (format_kind<_Rng> != range_format::disabled) +inline constexpr bool enable_nonlocking_formatter_optimization<_Rng> = false; #endif // _HAS_CXX23 _STD_END diff --git a/stl/inc/chrono b/stl/inc/chrono index 0a2db79cb6..ac194e5cfd 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5966,6 +5966,16 @@ struct formatter<_CHRONO zoned_time<_Duration, _TimeZonePtr>, _CharT> } }; +#if _HAS_CXX23 +template +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO duration<_Rep, _Period>> = + _STD enable_nonlocking_formatter_optimization<_Rep>; + +template +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO zoned_time<_Duration, const std::chrono::time_zone*>> = + true; +#endif + namespace chrono { template _NODISCARD string nonexistent_local_time::_Make_string(const local_time<_Duration>& _Tp, const local_info& _Info) { From 17ba44cd128462efede358022c6aa95cdbb27f82 Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Mon, 8 Jul 2024 00:11:29 +0200 Subject: [PATCH 02/58] Reduce duplication of vformat_to. --- stl/inc/format | 70 ++++++++++++-------------------------------------- 1 file changed, 16 insertions(+), 54 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index a53b3164b8..ce30485c7f 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -2199,9 +2199,6 @@ private: basic_format_args _Args; _Lazy_locale _Loc; - constexpr basic_format_context(_Out&& _OutputIt_, const basic_format_args& _Ctx_args) - : _OutputIt(_STD move(_OutputIt_)), _Args(_Ctx_args) {} - constexpr basic_format_context( _Out&& _OutputIt_, const basic_format_args& _Ctx_args, const _Lazy_locale& _Loc_) : _OutputIt(_STD move(_OutputIt_)), _Args(_Ctx_args), _Loc(_Loc_) {} @@ -2236,10 +2233,6 @@ public: return _Loc; } - _NODISCARD static constexpr basic_format_context _Make_from( - _Out _OutputIt_, basic_format_args _Ctx_args) { - return basic_format_context{_STD move(_OutputIt_), _Ctx_args}; - } _NODISCARD static constexpr basic_format_context _Make_from( _Out _OutputIt_, basic_format_args _Ctx_args, const _Lazy_locale& _Loc_) { return basic_format_context{_STD move(_OutputIt_), _Ctx_args, _Loc_}; @@ -3606,9 +3599,6 @@ struct _Format_handler { basic_format_parse_context<_CharT> _Parse_context; _Context _Ctx; - explicit _Format_handler(_OutputIt _Out, basic_string_view<_CharT> _Str, basic_format_args<_Context> _Format_args) - : _Parse_context(_Str), _Ctx(_Context::_Make_from(_STD move(_Out), _Format_args)) {} - explicit _Format_handler(_OutputIt _Out, basic_string_view<_CharT> _Str, basic_format_args<_Context> _Format_args, const _Lazy_locale& _Loc) : _Parse_context(_Str), _Ctx(_Context::_Make_from(_STD move(_Out), _Format_args, _Loc)) {} @@ -3735,68 +3725,40 @@ _NODISCARD auto make_wformat_args(_Args&... _Vals) { } _FMT_P2286_BEGIN -_EXPORT_STD template _OutputIt> -_OutputIt vformat_to(_OutputIt _Out, const string_view _Fmt, const format_args _Args) { - // Make `_Parse_format_string` type-dependent to defer instantiation: - using _Dependent_char = decltype((void) _Out, char{}); - if constexpr (is_same_v<_OutputIt, _Fmt_it>) { - _Format_handler<_Dependent_char> _Handler(_Out, _Fmt, _Args); +template _OutputIt, class _Context> +_OutputIt _Format_to_it(_OutputIt _Out, const basic_string_view<_CharT> _Fmt, const basic_format_args<_Context> _Args, + const _Lazy_locale _Loc) { + using _Fmt_it_char = _Basic_fmt_it<_CharT>; + if constexpr (is_same_v<_OutputIt, _Fmt_it_char>) { + _Format_handler<_CharT> _Handler(_Out, _Fmt, _Args, _Loc); _Parse_format_string(_Fmt, _Handler); return _Out; } else { - _Fmt_iterator_buffer<_OutputIt, char> _Buf(_STD move(_Out)); - _Format_handler<_Dependent_char> _Handler(_Fmt_it{_Buf}, _Fmt, _Args); + _Fmt_iterator_buffer<_OutputIt, _CharT> _Buf(_STD move(_Out)); + _Format_handler<_CharT> _Handler(_Fmt_it_char{_Buf}, _Fmt, _Args, _Loc); _Parse_format_string(_Fmt, _Handler); return _Buf._Out(); } } +_EXPORT_STD template _OutputIt> +_OutputIt vformat_to(_OutputIt _Out, const string_view _Fmt, const format_args _Args) { + return _Format_to_it(_STD move(_Out), _Fmt, _Args, _Lazy_locale{}); +} + _EXPORT_STD template _OutputIt> _OutputIt vformat_to(_OutputIt _Out, const wstring_view _Fmt, const wformat_args _Args) { - // Make `_Parse_format_string` type-dependent to defer instantiation: - using _Dependent_char = decltype((void) _Out, wchar_t{}); - if constexpr (is_same_v<_OutputIt, _Fmt_wit>) { - _Format_handler<_Dependent_char> _Handler(_Out, _Fmt, _Args); - _Parse_format_string(_Fmt, _Handler); - return _Out; - } else { - _Fmt_iterator_buffer<_OutputIt, wchar_t> _Buf(_STD move(_Out)); - _Format_handler<_Dependent_char> _Handler(_Fmt_wit{_Buf}, _Fmt, _Args); - _Parse_format_string(_Fmt, _Handler); - return _Buf._Out(); - } + return _Format_to_it(_STD move(_Out), _Fmt, _Args, _Lazy_locale{}); } _EXPORT_STD template _OutputIt> _OutputIt vformat_to(_OutputIt _Out, const locale& _Loc, const string_view _Fmt, const format_args _Args) { - // Make `_Parse_format_string` type-dependent to defer instantiation: - using _Dependent_char = decltype((void) _Out, char{}); - if constexpr (is_same_v<_OutputIt, _Fmt_it>) { - _Format_handler<_Dependent_char> _Handler(_Out, _Fmt, _Args, _Lazy_locale{_Loc}); - _Parse_format_string(_Fmt, _Handler); - return _Out; - } else { - _Fmt_iterator_buffer<_OutputIt, char> _Buf(_STD move(_Out)); - _Format_handler<_Dependent_char> _Handler(_Fmt_it{_Buf}, _Fmt, _Args, _Lazy_locale{_Loc}); - _Parse_format_string(_Fmt, _Handler); - return _Buf._Out(); - } + return _Format_to_it(_STD move(_Out), _Fmt, _Args, _Lazy_locale{_Loc}); } _EXPORT_STD template _OutputIt> _OutputIt vformat_to(_OutputIt _Out, const locale& _Loc, const wstring_view _Fmt, const wformat_args _Args) { - // Make `_Parse_format_string` type-dependent to defer instantiation: - using _Dependent_char = decltype((void) _Out, wchar_t{}); - if constexpr (is_same_v<_OutputIt, _Fmt_wit>) { - _Format_handler<_Dependent_char> _Handler(_Out, _Fmt, _Args, _Lazy_locale{_Loc}); - _Parse_format_string(_Fmt, _Handler); - return _Out; - } else { - _Fmt_iterator_buffer<_OutputIt, wchar_t> _Buf(_STD move(_Out)); - _Format_handler<_Dependent_char> _Handler(_Fmt_wit{_Buf}, _Fmt, _Args, _Lazy_locale{_Loc}); - _Parse_format_string(_Fmt, _Handler); - return _Buf._Out(); - } + return _Format_to_it(_STD move(_Out), _Fmt, _Args, _Lazy_locale{_Loc}); } _EXPORT_STD template _OutputIt, class... _Types> From 42efe6de2cc417efc86d01f7ce86676d0dbcd1f1 Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Mon, 8 Jul 2024 17:21:38 +0200 Subject: [PATCH 03/58] Make fmt flush behaviour customizable for optimizations. --- stl/inc/format | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index ce30485c7f..eef7548641 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -2348,14 +2348,12 @@ public: inline constexpr size_t _Fmt_buffer_size = 256; -template -struct _Back_insert_iterator_container_type { - using type = void; -}; - -template -struct _Back_insert_iterator_container_type> { - using type = _Container; +template +struct _Fmt_iterator_flush { + template + static _OutputIt _Flush(const _Ty* _First, const _Ty* _Last, _OutputIt _Output) { + return _STD _Copy_unchecked(_First, _Last, _STD move(_Output)); + } }; template @@ -2366,6 +2364,19 @@ struct _Back_insert_iterator_container_access : back_insert_iterator<_Container> using back_insert_iterator<_Container>::container; }; +template + requires (_Is_specialization_v<_Container, basic_string> || _Is_specialization_v<_Container, vector>) +struct _Fmt_iterator_flush> { + using _OutputIt = back_insert_iterator<_Container>; + + template + static _OutputIt _Flush(const _Ty* _First, const _Ty* _Last, _OutputIt _Output) { + _Container& _Cont = *_Back_insert_iterator_container_access<_Container>{_Output}.container; + _Cont.insert(_Cont.end(), _First, _Last); + return _Output; + } +}; + template class _Fmt_iterator_buffer final : public _Traits, public _Fmt_buffer<_Ty> { private: @@ -2383,14 +2394,7 @@ private: this->_Clear(); const auto _End = _Data + this->_Limit(_Size); - // extracts back_insert_iterator's underlying container type, or void if not. - using _Container = _Back_insert_iterator_container_type<_OutputIt>::type; - if constexpr (_Is_specialization_v<_Container, basic_string> || _Is_specialization_v<_Container, vector>) { - auto& _Cont = *_Back_insert_iterator_container_access<_Container>{_Output}.container; - _Cont.insert(_Cont.end(), _Data, _End); - } else { - _Output = _STD _Copy_unchecked(_Data, _End, _STD move(_Output)); - } + _Output = _Fmt_iterator_flush<_OutputIt>::_Flush(_Data, _End, _STD move(_Output)); } public: From 3c53aa0aa665b3ee998e9a56f6f25546dad39927 Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Mon, 8 Jul 2024 17:24:20 +0200 Subject: [PATCH 04/58] Print to stream and unicode console without extra string buffer when non-locking format types. --- stl/inc/print | 234 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 207 insertions(+), 27 deletions(-) diff --git a/stl/inc/print b/stl/inc/print index 13f12ea046..cc2be8fdb0 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -24,15 +24,130 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN -inline void _Print_noformat_nonunicode(FILE* const _Stream, const string_view _Str) { - const bool _Was_write_successful = _CSTD fwrite(_Str.data(), 1, _Str.size(), _Stream) == _Str.size(); +struct [[nodiscard]] _Stream_lock_guard { + explicit _Stream_lock_guard(FILE* const _Stream) : _Stream(_Stream) { + _CSTD _lock_file(_Stream); + } + ~_Stream_lock_guard() { + _CSTD _unlock_file(_Stream); + } + FILE* const _Stream; +}; + +class _Print_to_stream_it { +public: + using iterator_category = output_iterator_tag; + using difference_type = ptrdiff_t; + using value_type = void; + using pointer = void; + using reference = void; + + explicit _Print_to_stream_it(FILE* const _Stream) noexcept : _Stream(_Stream) {} + + template + _Print_to_stream_it& operator=(const _Ty&) { + return *this; + } + + _Print_to_stream_it& operator*() noexcept { + return *this; + } + + _Print_to_stream_it& operator++() noexcept { + return *this; + } + + _Print_to_stream_it operator++(int) noexcept { + return *this; + } + + FILE* _Get_stream() const noexcept { + return _Stream; + } + +private: + FILE* _Stream; +}; + +// Print nonunicode _Str to _Stream, caller must have locked _Stream +inline void _Print_noformat_nonunicode_nonlocking(FILE* const _Stream, const string_view _Str) { + const bool _Was_write_successful = _CSTD _fwrite_nolock(_Str.data(), 1, _Str.size(), _Stream) == _Str.size(); if (!_Was_write_successful) [[unlikely]] { _Throw_system_error(static_cast(errno)); } } -inline void _Vprint_nonunicode_impl( +template <> +struct _Fmt_iterator_flush<_Print_to_stream_it> { + static _Print_to_stream_it _Flush(const char* _First, const char* _Last, _Print_to_stream_it _Output) { + _STD _Print_noformat_nonunicode_nonlocking(_Output._Get_stream(), {_First, _Last}); + return _Output; + } +}; + +class _Print_to_unicode_console_it { +public: + using iterator_category = output_iterator_tag; + using difference_type = ptrdiff_t; + using value_type = void; + using pointer = void; + using reference = void; + + explicit _Print_to_unicode_console_it(const __std_unicode_console_handle _Console_handle) noexcept + : _Console_handle(_Console_handle) {} + + template + _Print_to_unicode_console_it& operator=(const _Ty&) { + return *this; + } + + _Print_to_unicode_console_it& operator*() noexcept { + return *this; + } + + _Print_to_unicode_console_it& operator++() noexcept { + return *this; + } + + _Print_to_unicode_console_it operator++(int) noexcept { + return *this; + } + + __std_unicode_console_handle _Get_console_handle() const noexcept { + return _Console_handle; + } + +private: + __std_unicode_console_handle _Console_handle; +}; + +// Print unicode _Str to unicode console handle, caller must have locked _Stream +inline void _Print_noformat_unicode_to_console_nonlocking( + const __std_unicode_console_handle _Console_handle, const string_view _Str) { + const __std_win_error _Console_print_result = + __std_print_to_unicode_console(_Console_handle, _Str.data(), _Str.size()); + if (_Console_print_result != __std_win_error::_Success) [[unlikely]] { + _STD _Throw_system_error_from_std_win_error(_Console_print_result); + } +} + +template <> +struct _Fmt_iterator_flush<_Print_to_unicode_console_it> { + static _Print_to_unicode_console_it _Flush( + const char* _First, const char* _Last, _Print_to_unicode_console_it _Output) { + _STD _Print_noformat_unicode_to_console_nonlocking(_Output._Get_console_handle(), {_First, _Last}); + return _Output; + } +}; + +inline void _Print_noformat_nonunicode(FILE* const _Stream, const string_view _Str) { + const _Stream_lock_guard _Guard{_Stream}; + _STD _Print_noformat_nonunicode_nonlocking(_Stream, _Str); +} + +// Format non unicode into a temporary string, then print to stream +inline void _Vprint_nonunicode_buffered_impl( const _Add_newline _Add_nl, FILE* const _Stream, const string_view _Fmt_str, const format_args _Fmt_args) { string _Output_str = _STD vformat(_Fmt_str, _Fmt_args); if (_Add_nl == _Add_newline::_Yes) { @@ -42,7 +157,18 @@ inline void _Vprint_nonunicode_impl( _STD _Print_noformat_nonunicode(_Stream, _Output_str); } -inline void _Print_noformat_unicode(FILE* const _Stream, const string_view _Str) { +// Format non unicode directly into _Stream +inline void _Vprint_nonunicode_impl( + const _Add_newline _Add_nl, FILE* const _Stream, const string_view _Fmt_str, const format_args _Fmt_args) { + const _Stream_lock_guard _Guard{_Stream}; + auto _Output_it = _STD vformat_to(_Print_to_stream_it{_Stream}, _Fmt_str, _Fmt_args); + if (_Add_nl == _Add_newline::_Yes) { + *_Output_it++ = '\n'; + } +} + +template +void _Do_on_maybe_unicode_console(FILE* const _Stream, _UnicodeConsoleFn _Unicode_console_func, _FallbackFn _Fallback_func) { const __std_unicode_console_retrieval_result _Unicode_console_retrieval_result{ __std_get_unicode_console_handle_from_file_stream(_Stream)}; @@ -69,47 +195,91 @@ inline void _Print_noformat_unicode(FILE* const _Stream, const string_view _Str) #pragma warning(pop) if (_Is_unicode_console) { - const bool _Was_flush_successful = _CSTD fflush(_Stream) == 0; + _Unicode_console_func(_Unicode_console_retrieval_result._Console_handle); + } else { + _Fallback_func(); + } +} + +// If a unicode console write using native API, else write to stream +inline void _Vprint_unicode_noformat_impl(FILE* const _Stream, const string_view _Output_str) { + const auto _Unicode_console = [&](const __std_unicode_console_handle _Console_handle) { + const _Stream_lock_guard _Guard{_Stream}; + + const bool _Was_flush_successful = _CSTD _fflush_nolock(_Stream) == 0; if (!_Was_flush_successful) [[unlikely]] { _Throw_system_error(static_cast(errno)); } - const __std_win_error _Console_print_result = - __std_print_to_unicode_console(_Unicode_console_retrieval_result._Console_handle, _Str.data(), _Str.size()); - if (_Console_print_result != __std_win_error::_Success) [[unlikely]] { - _STD _Throw_system_error_from_std_win_error(_Console_print_result); - } - } else { - _STD _Print_noformat_nonunicode(_Stream, _Str); - } + _STD _Print_noformat_unicode_to_console_nonlocking(_Console_handle, _Output_str); + }; + + const auto _Fallback = [&] { + _STD _Print_noformat_nonunicode(_Stream, _Output_str); + }; + + _STD _Do_on_maybe_unicode_console(_Stream, _Unicode_console, _Fallback); } -inline void _Vprint_unicode_impl( +// Format to intermediate string buffer, then print to unicode console/file +inline void _Vprint_unicode_buffered_impl( const _Add_newline _Add_nl, FILE* const _Stream, const string_view _Fmt_str, const format_args _Fmt_args) { string _Output_str = _STD vformat(_Fmt_str, _Fmt_args); if (_Add_nl == _Add_newline::_Yes) { _Output_str.push_back('\n'); } + _STD _Vprint_unicode_noformat_impl(_Stream, _Output_str); +} + +void _Vprint_unicode_impl( + const _Add_newline _Add_nl, FILE* const _Stream, const string_view _Fmt_str, const format_args _Fmt_args) { + const auto _Unicode_console = [&](const __std_unicode_console_handle _Console_handle) { + const _Stream_lock_guard _Guard{_Stream}; + + const bool _Was_flush_successful = _CSTD _fflush_nolock(_Stream) == 0; + if (!_Was_flush_successful) [[unlikely]] { + _Throw_system_error(static_cast(errno)); + } - _STD _Print_noformat_unicode(_Stream, _Output_str); + auto _Output_it = _STD vformat_to(_Print_to_unicode_console_it{_Console_handle}, _Fmt_str, _Fmt_args); + if (_Add_nl == _Add_newline::_Yes) { + *_Output_it++ = '\n'; + } + }; + + const auto _Fallback = [&] { + _STD _Vprint_nonunicode_impl( _Add_nl, _Stream, _Fmt_str, _Fmt_args ); + }; + + _STD _Do_on_maybe_unicode_console(_Stream, _Unicode_console, _Fallback); } template void _Print_impl( const _Add_newline _Add_nl, FILE* const _Stream, const format_string<_Types...> _Fmt, _Types&&... _Args) { - constexpr bool _Has_format_args = sizeof...(_Types) > 0; + constexpr bool _Has_format_args = sizeof...(_Types) > 0; + constexpr bool _Print_nonlocking = + (_STD enable_nonlocking_formatter_optimization<_STD remove_cvref_t<_Types>> && ...); if constexpr (_Has_format_args) { if constexpr (_STD _Is_ordinary_literal_encoding_utf8()) { - _STD _Vprint_unicode_impl(_Add_nl, _Stream, _Fmt.get(), _STD make_format_args(_Args...)); + if constexpr (_Print_nonlocking) { + _STD _Vprint_unicode_impl(_Add_nl, _Stream, _Fmt.get(), _STD make_format_args(_Args...)); + } else { + _STD _Vprint_unicode_buffered_impl(_Add_nl, _Stream, _Fmt.get(), _STD make_format_args(_Args...)); + } } else { - _STD _Vprint_nonunicode_impl(_Add_nl, _Stream, _Fmt.get(), _STD make_format_args(_Args...)); + if constexpr (_Print_nonlocking) { + _STD _Vprint_nonunicode_impl(_Add_nl, _Stream, _Fmt.get(), _STD make_format_args(_Args...)); + } else { + _STD _Vprint_nonunicode_buffered_impl(_Add_nl, _Stream, _Fmt.get(), _STD make_format_args(_Args...)); + } } } else { const string _Unescaped_str{_Unescape_braces(_Add_nl, _Fmt.get())}; if constexpr (_STD _Is_ordinary_literal_encoding_utf8()) { - _STD _Print_noformat_unicode(_Stream, _Unescaped_str); + _STD _Vprint_unicode_noformat_impl(_Stream, _Unescaped_str); } else { _STD _Print_noformat_nonunicode(_Stream, _Unescaped_str); } @@ -147,23 +317,33 @@ void println(const format_string<_Types...> _Fmt, _Types&&... _Args) { } _EXPORT_STD template // improves throughput, see GH-2329 -void vprint_unicode(FILE* const _Stream, const string_view _Fmt_str, const format_args _Fmt_args) { - _STD _Vprint_unicode_impl(_Add_newline::_Nope, _Stream, _Fmt_str, _Fmt_args); +void vprint_unicode_buffered(FILE* const _Stream, const string_view _Fmt_str, const format_args _Fmt_args) { + _STD _Vprint_unicode_buffered_impl(_Add_newline::_Nope, _Stream, _Fmt_str, _Fmt_args); } _EXPORT_STD template // improves throughput, see GH-2329 -void vprint_unicode(const string_view _Fmt_str, const format_args _Fmt_args) { - _STD vprint_unicode(stdout, _Fmt_str, _Fmt_args); +void vprint_unicode_buffered(const string_view _Fmt_str, const format_args _Fmt_args) { + _STD vprint_unicode_buffered(stdout, _Fmt_str, _Fmt_args); } _EXPORT_STD template // improves throughput, see GH-2329 -void vprint_nonunicode(FILE* const _Stream, const string_view _Fmt_str, const format_args _Fmt_args) { - _STD _Vprint_nonunicode_impl(_Add_newline::_Nope, _Stream, _Fmt_str, _Fmt_args); +void vprint_nonunicode_buffered(FILE* const _Stream, const string_view _Fmt_str, const format_args _Fmt_args) { + _STD _Vprint_nonunicode_buffered_impl(_Add_newline::_Nope, _Stream, _Fmt_str, _Fmt_args); +} + +_EXPORT_STD template // improves throughput, see GH-2329 +void vprint_nonunicode_buffered(const string_view _Fmt_str, const format_args _Fmt_args) { + _STD vprint_nonunicode_buffered(stdout, _Fmt_str, _Fmt_args); } _EXPORT_STD template // improves throughput, see GH-2329 -void vprint_nonunicode(const string_view _Fmt_str, const format_args _Fmt_args) { - _STD vprint_nonunicode(stdout, _Fmt_str, _Fmt_args); +void vprint_unicode(FILE* const _Stream, const string_view _Fmt_str, const format_args _Fmt_args) { + _STD _Vprint_unicode_impl(_Add_newline::_Nope, _Stream, _Fmt_str, _Fmt_args); +} + +_EXPORT_STD template // improves throughput, see GH-2329 +void vprint_nonunicode(FILE* const _Stream, const string_view _Fmt_str, const format_args _Fmt_args) { + _STD _Vprint_nonunicode_impl(_Add_newline::_Nope, _Stream, _Fmt_str, _Fmt_args); } _STD_END From 0872f1edff77915c32b69c2e4431a6931490fbcb Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Mon, 8 Jul 2024 17:26:32 +0200 Subject: [PATCH 05/58] Use an initial inline buffer for transcoding, will not require growing in many cases. --- stl/src/print.cpp | 54 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/stl/src/print.cpp b/stl/src/print.cpp index fcd7d263e3..ed8df50604 100644 --- a/stl/src/print.cpp +++ b/stl/src/print.cpp @@ -59,25 +59,53 @@ namespace { public: _Allocated_string() = default; - explicit _Allocated_string(__crt_unique_heap_ptr&& _Other_str, const size_t _Other_capacity) noexcept - : _Str(_STD move(_Other_str)), _Str_capacity(_Other_capacity) {} + ~_Allocated_string() { + if (_Using_heap()) { + _Str.release(); + } + } - [[nodiscard]] wchar_t* _Data() const noexcept { - return _Str.get(); + [[nodiscard]] wchar_t* _Data() noexcept { + return _Using_heap() ? _Str.get() : _Buffer; } [[nodiscard]] size_t _Capacity() const noexcept { return _Str_capacity; } - void _Reset() noexcept { + bool _Grow(const size_t _Capacity) { + if (_Capacity <= _Str_capacity) { + return true; + } + _Str.release(); _Str_capacity = 0; + + __crt_unique_heap_ptr _Wide_str{_malloc_crt_t(wchar_t, _Capacity)}; + if (!_Wide_str) [[unlikely]] { + return false; + } + + _Str = std::move(_Wide_str); + _Str_capacity = _Capacity; + + return true; } private: - __crt_unique_heap_ptr _Str; - size_t _Str_capacity = 0; + // Allows small formatted strings, such as those from _Print_to_unicode_console_it, to not allocate any extra + // internal transcoding buffer + static constexpr size_t _Buffer_size = 2048; + + bool _Using_heap() const { + return _Str_capacity > _Buffer_size; + } + + union { + wchar_t _Buffer[_Buffer_size]{}; + __crt_unique_heap_ptr _Str; + }; + size_t _Str_capacity = _Buffer_size; }; template @@ -187,15 +215,9 @@ namespace { return static_cast<__std_win_error>(GetLastError()); } - if (static_cast(_Num_chars_required) > _Dst_str._Capacity()) { - _Dst_str._Reset(); - - __crt_unique_heap_ptr _Wide_str{_malloc_crt_t(wchar_t, _Num_chars_required)}; - if (!_Wide_str) [[unlikely]] { - return __std_win_error::_Not_enough_memory; - } - - _Dst_str = _Allocated_string{_STD move(_Wide_str), static_cast(_Num_chars_required)}; + const bool _Has_space = _Dst_str._Grow(static_cast(_Num_chars_required)); + if (!_Has_space) [[unlikely]] { + return __std_win_error::_Not_enough_memory; } const int32_t _Conversion_result = MultiByteToWideChar(CP_UTF8, 0, _Src_str._Data(), From 6e56a61bb0b931d8b7e4d047fb1954bd8f0038d3 Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Mon, 8 Jul 2024 17:27:14 +0200 Subject: [PATCH 06/58] Add print benchmarks. --- benchmarks/CMakeLists.txt | 1 + benchmarks/src/print.cpp | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 benchmarks/src/print.cpp diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 7c98173e06..8c61ca714b 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -116,6 +116,7 @@ add_benchmark(locale_classic src/locale_classic.cpp) add_benchmark(minmax_element src/minmax_element.cpp) add_benchmark(mismatch src/mismatch.cpp) add_benchmark(path_lexically_normal src/path_lexically_normal.cpp) +add_benchmark(print src/print.cpp) add_benchmark(priority_queue_push_range src/priority_queue_push_range.cpp) add_benchmark(random_integer_generation src/random_integer_generation.cpp) add_benchmark(replace src/replace.cpp) diff --git a/benchmarks/src/print.cpp b/benchmarks/src/print.cpp new file mode 100644 index 0000000000..1a13026c96 --- /dev/null +++ b/benchmarks/src/print.cpp @@ -0,0 +1,32 @@ +#include +#include + +namespace { + using PrintType = void (*)(FILE*, std::string_view, std::format_args); + + template + void BM_vprint(benchmark::State& state) { + for (auto _ : state) { + PrintFunction(stdout, "Hello cool unicode 😊😊😊\n", std::make_format_args()); + } + } + BENCHMARK(BM_vprint<&std::vprint_unicode>); + BENCHMARK(BM_vprint<&std::vprint_unicode_buffered>); + + template + void BM_vprint_complex(benchmark::State& state) { + const int i = 42; + const std::string str = "Hello world 😊😊😊😊😊😊😊😊😊😊😊😊😊"; + const double f = -902.16283758; + const std::pair p{16, 2.073f}; + for (auto _ : state) { + PrintFunction(stdout, + "Hello cool unicode 😊😊😊 {:X}, {}, {:a}, I am a big string, lots of lines, multiple {} formats\n", + std::make_format_args(i, str, f, p)); + } + } + BENCHMARK(BM_vprint_complex<&std::vprint_unicode>); + BENCHMARK(BM_vprint_complex<&std::vprint_unicode_buffered>); +} // namespace + +BENCHMARK_MAIN(); From 60f3630dbd5302de88d067ea8bcb685913ec8fab Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Mon, 8 Jul 2024 17:41:39 +0200 Subject: [PATCH 07/58] Update feature test macro. --- stl/inc/yvals_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index bd1a562828..65cbfaf183 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -1767,7 +1767,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #define __cpp_lib_mdspan 202207L #define __cpp_lib_move_only_function 202110L #define __cpp_lib_out_ptr 202311L -#define __cpp_lib_print 202207L +#define __cpp_lib_print 202406L #define __cpp_lib_ranges_as_const 202311L #define __cpp_lib_ranges_as_rvalue 202207L #define __cpp_lib_ranges_cartesian_product 202207L From bd072d5b8b085a8c94d81ccc6d3e8f35ab6122a7 Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Mon, 8 Jul 2024 18:49:50 +0200 Subject: [PATCH 08/58] Fix formatting. --- stl/inc/chrono | 4 ++-- stl/inc/format | 2 +- stl/inc/print | 17 +++++++---------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index ac194e5cfd..c42b6c23d9 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5972,8 +5972,8 @@ inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO duration< _STD enable_nonlocking_formatter_optimization<_Rep>; template -inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO zoned_time<_Duration, const std::chrono::time_zone*>> = - true; +inline constexpr bool + enable_nonlocking_formatter_optimization<_CHRONO zoned_time<_Duration, const std::chrono::time_zone*>> = true; #endif namespace chrono { diff --git a/stl/inc/format b/stl/inc/format index eef7548641..dd606d741c 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -2350,7 +2350,7 @@ inline constexpr size_t _Fmt_buffer_size = 256; template struct _Fmt_iterator_flush { - template + template static _OutputIt _Flush(const _Ty* _First, const _Ty* _Last, _OutputIt _Output) { return _STD _Copy_unchecked(_First, _Last, _STD move(_Output)); } diff --git a/stl/inc/print b/stl/inc/print index cc2be8fdb0..904f4101de 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -167,8 +167,9 @@ inline void _Vprint_nonunicode_impl( } } -template -void _Do_on_maybe_unicode_console(FILE* const _Stream, _UnicodeConsoleFn _Unicode_console_func, _FallbackFn _Fallback_func) { +template +void _Do_on_maybe_unicode_console( + FILE* const _Stream, _UnicodeConsoleFn _Unicode_console_func, _FallbackFn _Fallback_func) { const __std_unicode_console_retrieval_result _Unicode_console_retrieval_result{ __std_get_unicode_console_handle_from_file_stream(_Stream)}; @@ -214,9 +215,7 @@ inline void _Vprint_unicode_noformat_impl(FILE* const _Stream, const string_view _STD _Print_noformat_unicode_to_console_nonlocking(_Console_handle, _Output_str); }; - const auto _Fallback = [&] { - _STD _Print_noformat_nonunicode(_Stream, _Output_str); - }; + const auto _Fallback = [&] { _STD _Print_noformat_nonunicode(_Stream, _Output_str); }; _STD _Do_on_maybe_unicode_console(_Stream, _Unicode_console, _Fallback); } @@ -247,17 +246,15 @@ void _Vprint_unicode_impl( } }; - const auto _Fallback = [&] { - _STD _Vprint_nonunicode_impl( _Add_nl, _Stream, _Fmt_str, _Fmt_args ); - }; - + const auto _Fallback = [&] { _STD _Vprint_nonunicode_impl(_Add_nl, _Stream, _Fmt_str, _Fmt_args); }; + _STD _Do_on_maybe_unicode_console(_Stream, _Unicode_console, _Fallback); } template void _Print_impl( const _Add_newline _Add_nl, FILE* const _Stream, const format_string<_Types...> _Fmt, _Types&&... _Args) { - constexpr bool _Has_format_args = sizeof...(_Types) > 0; + constexpr bool _Has_format_args = sizeof...(_Types) > 0; constexpr bool _Print_nonlocking = (_STD enable_nonlocking_formatter_optimization<_STD remove_cvref_t<_Types>> && ...); From 62a983420708171e62b6726548f3789d740194e4 Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Mon, 8 Jul 2024 18:50:10 +0200 Subject: [PATCH 09/58] Be slightly more language correct and use the unique_ptr destructor. --- stl/src/print.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/src/print.cpp b/stl/src/print.cpp index ed8df50604..8307e4df05 100644 --- a/stl/src/print.cpp +++ b/stl/src/print.cpp @@ -61,7 +61,7 @@ namespace { ~_Allocated_string() { if (_Using_heap()) { - _Str.release(); + _Str.~__crt_unique_heap_ptr(); } } From 211c8d9d37d1fc2a0366cac34acfde1c2d69ec6f Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Mon, 8 Jul 2024 18:59:55 +0200 Subject: [PATCH 10/58] No unicode in source files. --- benchmarks/src/print.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/benchmarks/src/print.cpp b/benchmarks/src/print.cpp index 1a13026c96..27aa8aa721 100644 --- a/benchmarks/src/print.cpp +++ b/benchmarks/src/print.cpp @@ -1,4 +1,4 @@ -#include +#include #include namespace { @@ -7,7 +7,7 @@ namespace { template void BM_vprint(benchmark::State& state) { for (auto _ : state) { - PrintFunction(stdout, "Hello cool unicode 😊😊😊\n", std::make_format_args()); + PrintFunction(stdout, "Hello cool I am going to print as unicode\n", std::make_format_args()); } } BENCHMARK(BM_vprint<&std::vprint_unicode>); @@ -16,12 +16,13 @@ namespace { template void BM_vprint_complex(benchmark::State& state) { const int i = 42; - const std::string str = "Hello world 😊😊😊😊😊😊😊😊😊😊😊😊😊"; + const std::string str = "Hello world!!!!!!!!!!!!!!!!!!!!!!!!"; const double f = -902.16283758; const std::pair p{16, 2.073f}; for (auto _ : state) { PrintFunction(stdout, - "Hello cool unicode 😊😊😊 {:X}, {}, {:a}, I am a big string, lots of lines, multiple {} formats\n", + "Hello cool I am going print as unicode!! {:X}, {}, {:a}, I am a big string, lots of words, multiple " + "{} formats\n", std::make_format_args(i, str, f, p)); } } From 1f33e667ca8fcd3e07ce4991326601a2dde0b347 Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Mon, 8 Jul 2024 19:00:12 +0200 Subject: [PATCH 11/58] Add more const --- stl/inc/print | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/print b/stl/inc/print index 904f4101de..8205f67502 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -80,7 +80,7 @@ inline void _Print_noformat_nonunicode_nonlocking(FILE* const _Stream, const str template <> struct _Fmt_iterator_flush<_Print_to_stream_it> { - static _Print_to_stream_it _Flush(const char* _First, const char* _Last, _Print_to_stream_it _Output) { + static _Print_to_stream_it _Flush(const char* const _First, const char* const _Last, _Print_to_stream_it _Output) { _STD _Print_noformat_nonunicode_nonlocking(_Output._Get_stream(), {_First, _Last}); return _Output; } @@ -135,7 +135,7 @@ inline void _Print_noformat_unicode_to_console_nonlocking( template <> struct _Fmt_iterator_flush<_Print_to_unicode_console_it> { static _Print_to_unicode_console_it _Flush( - const char* _First, const char* _Last, _Print_to_unicode_console_it _Output) { + const char* const _First, const char* const _Last, _Print_to_unicode_console_it _Output) { _STD _Print_noformat_unicode_to_console_nonlocking(_Output._Get_console_handle(), {_First, _Last}); return _Output; } From 15b7e377287acc410e8bc15fafd84eef6bf95b8c Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Mon, 8 Jul 2024 19:00:56 +0200 Subject: [PATCH 12/58] Add comment explaining function name. --- stl/src/print.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/stl/src/print.cpp b/stl/src/print.cpp index 8307e4df05..471e397535 100644 --- a/stl/src/print.cpp +++ b/stl/src/print.cpp @@ -78,6 +78,7 @@ namespace { return true; } + // This is actually a reset in std::unique_ptr terms, it deallocates the memory _Str.release(); _Str_capacity = 0; From ac1bc82dbf22313c4158241699661928f4d1eef5 Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Mon, 8 Jul 2024 20:35:35 +0200 Subject: [PATCH 13/58] Add more const --- stl/inc/format | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index dd606d741c..0d394e0317 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -2351,7 +2351,7 @@ inline constexpr size_t _Fmt_buffer_size = 256; template struct _Fmt_iterator_flush { template - static _OutputIt _Flush(const _Ty* _First, const _Ty* _Last, _OutputIt _Output) { + static _OutputIt _Flush(const _Ty* const _First, const _Ty* const _Last, _OutputIt _Output) { return _STD _Copy_unchecked(_First, _Last, _STD move(_Output)); } }; @@ -2370,7 +2370,7 @@ struct _Fmt_iterator_flush> { using _OutputIt = back_insert_iterator<_Container>; template - static _OutputIt _Flush(const _Ty* _First, const _Ty* _Last, _OutputIt _Output) { + static _OutputIt _Flush(const _Ty* const _First, const _Ty* const _Last, _OutputIt _Output) { _Container& _Cont = *_Back_insert_iterator_container_access<_Container>{_Output}.container; _Cont.insert(_Cont.end(), _First, _Last); return _Output; From 5ca4c1e8b475689e369cc7884cc2d24385c0175d Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Mon, 8 Jul 2024 20:35:45 +0200 Subject: [PATCH 14/58] Update test macro usage --- .../tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 43419dabc7..9b56190a75 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -702,7 +702,7 @@ STATIC_ASSERT(__cpp_lib_polymorphic_allocator == 201902L); #endif #if _HAS_CXX23 -STATIC_ASSERT(__cpp_lib_print == 202207L); +STATIC_ASSERT(__cpp_lib_print == 202406L); #elif defined(__cpp_lib_print) #error __cpp_lib_print is defined #endif From d18688f85e0222aefb57bc78adc1d7b6ef90dbcb Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Mon, 8 Jul 2024 21:52:39 +0200 Subject: [PATCH 15/58] Add missing inline --- stl/inc/print | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/print b/stl/inc/print index 8205f67502..76266c702a 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -230,7 +230,7 @@ inline void _Vprint_unicode_buffered_impl( _STD _Vprint_unicode_noformat_impl(_Stream, _Output_str); } -void _Vprint_unicode_impl( +inline void _Vprint_unicode_impl( const _Add_newline _Add_nl, FILE* const _Stream, const string_view _Fmt_str, const format_args _Fmt_args) { const auto _Unicode_console = [&](const __std_unicode_console_handle _Console_handle) { const _Stream_lock_guard _Guard{_Stream}; From 9f8f9fa9b76a9f19b4cb36c00e32e0180599f3d6 Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Tue, 9 Jul 2024 00:07:31 +0200 Subject: [PATCH 16/58] Fix trailing newline print. --- stl/inc/print | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/stl/inc/print b/stl/inc/print index 76266c702a..d07020196a 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -44,8 +44,7 @@ public: explicit _Print_to_stream_it(FILE* const _Stream) noexcept : _Stream(_Stream) {} - template - _Print_to_stream_it& operator=(const _Ty&) { + _Print_to_stream_it& operator=(const char) { return *this; } @@ -97,8 +96,7 @@ public: explicit _Print_to_unicode_console_it(const __std_unicode_console_handle _Console_handle) noexcept : _Console_handle(_Console_handle) {} - template - _Print_to_unicode_console_it& operator=(const _Ty&) { + _Print_to_unicode_console_it& operator=(const char) { return *this; } @@ -161,9 +159,9 @@ inline void _Vprint_nonunicode_buffered_impl( inline void _Vprint_nonunicode_impl( const _Add_newline _Add_nl, FILE* const _Stream, const string_view _Fmt_str, const format_args _Fmt_args) { const _Stream_lock_guard _Guard{_Stream}; - auto _Output_it = _STD vformat_to(_Print_to_stream_it{_Stream}, _Fmt_str, _Fmt_args); + _STD vformat_to(_Print_to_stream_it{_Stream}, _Fmt_str, _Fmt_args); if (_Add_nl == _Add_newline::_Yes) { - *_Output_it++ = '\n'; + _Print_noformat_nonunicode_nonlocking(_Stream, "\n"); } } @@ -240,9 +238,9 @@ inline void _Vprint_unicode_impl( _Throw_system_error(static_cast(errno)); } - auto _Output_it = _STD vformat_to(_Print_to_unicode_console_it{_Console_handle}, _Fmt_str, _Fmt_args); + _STD vformat_to(_Print_to_unicode_console_it{_Console_handle}, _Fmt_str, _Fmt_args); if (_Add_nl == _Add_newline::_Yes) { - *_Output_it++ = '\n'; + _Print_noformat_unicode_to_console_nonlocking(_Console_handle, "\n"); } }; From f01f04b76c6e9e5f1b274c25423dd9faa179fc0f Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Tue, 9 Jul 2024 20:45:01 +0200 Subject: [PATCH 17/58] Fix nodsicard and noexcept --- stl/inc/print | 2 +- stl/src/print.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/print b/stl/inc/print index d07020196a..5bb9e0022c 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -24,7 +24,7 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN -struct [[nodiscard]] _Stream_lock_guard { +struct _NODISCARD _Stream_lock_guard { explicit _Stream_lock_guard(FILE* const _Stream) : _Stream(_Stream) { _CSTD _lock_file(_Stream); } diff --git a/stl/src/print.cpp b/stl/src/print.cpp index 471e397535..54cff60cc9 100644 --- a/stl/src/print.cpp +++ b/stl/src/print.cpp @@ -98,7 +98,7 @@ namespace { // internal transcoding buffer static constexpr size_t _Buffer_size = 2048; - bool _Using_heap() const { + bool _Using_heap() const noexcept { return _Str_capacity > _Buffer_size; } From 8a83b062e5878027e52f00950c1200b3d7ab61d8 Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Tue, 9 Jul 2024 11:42:57 +0200 Subject: [PATCH 18/58] Update expected_results.txt --- tests/libcxx/expected_results.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 1b16cd1501..405fff9874 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -85,6 +85,9 @@ std/language.support/support.limits/support.limits.general/algorithm.version.com std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp FAIL std/language.support/support.limits/support.limits.general/iterator.version.compile.pass.cpp FAIL +# Test expects __cpp_lib_print to have the old value 202207L for P2093R14; we define the C++23 value 202406L for P3107R5. +std/language.support/support.limits/support.limits.general/print.version.compile.pass.cpp FAIL + # libc++ tests strengthened assignment operators (not compatible with P2165R4: "Compatibility Between tuple, pair, And tuple-like Objects") std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp FAIL From dbc0281db877e5f6df29e3fee44498970561b368 Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Tue, 9 Jul 2024 12:54:47 +0200 Subject: [PATCH 19/58] Update for print implementation --- tests/libcxx/expected_results.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 405fff9874..265f2bcd9d 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -87,6 +87,7 @@ std/language.support/support.limits/support.limits.general/iterator.version.comp # Test expects __cpp_lib_print to have the old value 202207L for P2093R14; we define the C++23 value 202406L for P3107R5. std/language.support/support.limits/support.limits.general/print.version.compile.pass.cpp FAIL +std/language.support/support.limits/support.limits.general/ostream.version.compile.pass.cpp FAIL # libc++ tests strengthened assignment operators (not compatible with P2165R4: "Compatibility Between tuple, pair, And tuple-like Objects") std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp FAIL From dea2f8cb187ac4a56fef39fef6d338567d0a6bbd Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Tue, 9 Jul 2024 20:50:52 +0200 Subject: [PATCH 20/58] malloc fails via nullptr so this is also noexcept --- stl/src/print.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/src/print.cpp b/stl/src/print.cpp index 54cff60cc9..ca4b73929f 100644 --- a/stl/src/print.cpp +++ b/stl/src/print.cpp @@ -73,7 +73,7 @@ namespace { return _Str_capacity; } - bool _Grow(const size_t _Capacity) { + bool _Grow(const size_t _Capacity) noexcept { if (_Capacity <= _Str_capacity) { return true; } From 4bc8927f368e62053be5a5502ee0e8510fb8e513 Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Wed, 10 Jul 2024 11:44:26 +0200 Subject: [PATCH 21/58] Drop inline, not required on template vars and unobservable difference to standard spec per MSVC convention. --- stl/inc/__msvc_formatter.hpp | 18 +++++++++--------- stl/inc/chrono | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/stl/inc/__msvc_formatter.hpp b/stl/inc/__msvc_formatter.hpp index 7c477a4385..530be1ec79 100644 --- a/stl/inc/__msvc_formatter.hpp +++ b/stl/inc/__msvc_formatter.hpp @@ -376,34 +376,34 @@ template <_Format_supported_charT _CharT, class... _Types> struct formatter, _CharT>; template <_Format_supported_charT _CharT> -inline constexpr bool enable_nonlocking_formatter_optimization<_CharT> = true; +constexpr bool enable_nonlocking_formatter_optimization<_CharT> = true; template <_Format_supported_charT _CharT> -inline constexpr bool enable_nonlocking_formatter_optimization<_CharT*> = true; +constexpr bool enable_nonlocking_formatter_optimization<_CharT*> = true; template <_Format_supported_charT _CharT> -inline constexpr bool enable_nonlocking_formatter_optimization = true; +constexpr bool enable_nonlocking_formatter_optimization = true; template <_Format_supported_charT _CharT, size_t _Nx> -inline constexpr bool enable_nonlocking_formatter_optimization<_CharT[_Nx]> = true; +constexpr bool enable_nonlocking_formatter_optimization<_CharT[_Nx]> = true; template <_Format_supported_charT _CharT, class _Traits, class _Allocator> -inline constexpr bool enable_nonlocking_formatter_optimization> = true; +constexpr bool enable_nonlocking_formatter_optimization> = true; template <_Format_supported_charT _CharT, class _Traits> -inline constexpr bool enable_nonlocking_formatter_optimization> = true; +constexpr bool enable_nonlocking_formatter_optimization> = true; template -inline constexpr bool enable_nonlocking_formatter_optimization> = +constexpr bool enable_nonlocking_formatter_optimization> = _STD enable_nonlocking_formatter_optimization<_Ty1> && _STD enable_nonlocking_formatter_optimization<_Ty2>; template -inline constexpr bool enable_nonlocking_formatter_optimization> = +constexpr bool enable_nonlocking_formatter_optimization> = (_STD enable_nonlocking_formatter_optimization<_Ts> && ...); template <_RANGES input_range _Rng> requires (format_kind<_Rng> != range_format::disabled) -inline constexpr bool enable_nonlocking_formatter_optimization<_Rng> = false; +constexpr bool enable_nonlocking_formatter_optimization<_Rng> = false; #endif // _HAS_CXX23 _STD_END diff --git a/stl/inc/chrono b/stl/inc/chrono index c42b6c23d9..e2c67052a5 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5968,12 +5968,12 @@ struct formatter<_CHRONO zoned_time<_Duration, _TimeZonePtr>, _CharT> #if _HAS_CXX23 template -inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO duration<_Rep, _Period>> = +constexpr bool enable_nonlocking_formatter_optimization<_CHRONO duration<_Rep, _Period>> = _STD enable_nonlocking_formatter_optimization<_Rep>; template -inline constexpr bool - enable_nonlocking_formatter_optimization<_CHRONO zoned_time<_Duration, const std::chrono::time_zone*>> = true; +constexpr bool enable_nonlocking_formatter_optimization<_CHRONO zoned_time<_Duration, const std::chrono::time_zone*>> = + true; #endif namespace chrono { From c870a52bf6af6280fa80fe1a5d7cfd4ce52fbf00 Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Thu, 29 Aug 2024 20:01:25 +0200 Subject: [PATCH 22/58] Optimize and improve _Allocated_string --- stl/src/print.cpp | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/stl/src/print.cpp b/stl/src/print.cpp index ca4b73929f..35e6eb9345 100644 --- a/stl/src/print.cpp +++ b/stl/src/print.cpp @@ -57,11 +57,11 @@ extern "C" { namespace { class _Allocated_string { public: - _Allocated_string() = default; + _Allocated_string() noexcept : _Buffer(L'\0') {} ~_Allocated_string() { if (_Using_heap()) { - _Str.~__crt_unique_heap_ptr(); + _Str.~_Heap_string(); } } @@ -73,27 +73,30 @@ namespace { return _Str_capacity; } - bool _Grow(const size_t _Capacity) noexcept { + [[nodiscard]] bool _Grow(const size_t _Capacity) noexcept { if (_Capacity <= _Str_capacity) { return true; } - // This is actually a reset in std::unique_ptr terms, it deallocates the memory - _Str.release(); - _Str_capacity = 0; + if (_Using_heap()) { + _Str.~_Heap_string(); + } - __crt_unique_heap_ptr _Wide_str{_malloc_crt_t(wchar_t, _Capacity)}; - if (!_Wide_str) [[unlikely]] { + ::new (&_Str) _Heap_string(_Wide_str{_malloc_crt_t(wchar_t, _Capacity)}; + + if (!_Str) [[unlikely]] { + _Str_capacity = _Buffer_size; + _Buffer[0] = L'\0'; // Activiate inline buffer member return false; } - _Str = std::move(_Wide_str); _Str_capacity = _Capacity; - return true; } private: + using _Heap_string = __crt_unique_heap_ptr; + // Allows small formatted strings, such as those from _Print_to_unicode_console_it, to not allocate any extra // internal transcoding buffer static constexpr size_t _Buffer_size = 2048; @@ -102,11 +105,11 @@ namespace { return _Str_capacity > _Buffer_size; } + size_t _Str_capacity = _Buffer_size; union { - wchar_t _Buffer[_Buffer_size]{}; - __crt_unique_heap_ptr _Str; + wchar_t _Buffer[_Buffer_size]; + _Heap_string _Str; }; - size_t _Str_capacity = _Buffer_size; }; template From 4cee26ee530eddbc0f6e2b0a02a2a77eb2ce3912 Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Thu, 29 Aug 2024 20:01:34 +0200 Subject: [PATCH 23/58] Add paper comments --- stl/inc/yvals_core.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 65cbfaf183..6c3520a5ea 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -395,6 +395,8 @@ // (except for __cpp_lib_span which also covers C++26 span::at) // P2836R1 basic_const_iterator Should Follow Its Underlying Type's Convertibility // P3142R0 Printing Blank Lines With println() +// P3107R5 Permit An Efficient Implementation Of +// P3235R3 std::print More Types Faster With Less Memory // _HAS_CXX23 and _SILENCE_ALL_CXX23_DEPRECATION_WARNINGS control: // P1413R3 Deprecate aligned_storage And aligned_union From 58deae6b54c48965f8731c89b749eed964f7d17d Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Thu, 29 Aug 2024 20:01:41 +0200 Subject: [PATCH 24/58] Order tests alphabetically --- tests/libcxx/expected_results.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 265f2bcd9d..9e33b6b4f1 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -86,8 +86,8 @@ std/language.support/support.limits/support.limits.general/functional.version.co std/language.support/support.limits/support.limits.general/iterator.version.compile.pass.cpp FAIL # Test expects __cpp_lib_print to have the old value 202207L for P2093R14; we define the C++23 value 202406L for P3107R5. -std/language.support/support.limits/support.limits.general/print.version.compile.pass.cpp FAIL std/language.support/support.limits/support.limits.general/ostream.version.compile.pass.cpp FAIL +std/language.support/support.limits/support.limits.general/print.version.compile.pass.cpp FAIL # libc++ tests strengthened assignment operators (not compatible with P2165R4: "Compatibility Between tuple, pair, And tuple-like Objects") std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp FAIL From b781613795ea8d8f2e702211c32dc9e5b16d913a Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Thu, 29 Aug 2024 20:02:41 +0200 Subject: [PATCH 25/58] Give more specific name to benchmark file --- benchmarks/CMakeLists.txt | 2 +- benchmarks/src/{print.cpp => efficient_nonlocking_print.cpp} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename benchmarks/src/{print.cpp => efficient_nonlocking_print.cpp} (100%) diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 8c61ca714b..98953e0771 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -109,6 +109,7 @@ function(add_benchmark name) endfunction() add_benchmark(bitset_to_string src/bitset_to_string.cpp) +add_benchmark(efficient_nonlocking_print src/efficient_nonlocking_print.cpp) add_benchmark(find_and_count src/find_and_count.cpp) add_benchmark(find_first_of src/find_first_of.cpp) add_benchmark(iota src/iota.cpp) @@ -116,7 +117,6 @@ add_benchmark(locale_classic src/locale_classic.cpp) add_benchmark(minmax_element src/minmax_element.cpp) add_benchmark(mismatch src/mismatch.cpp) add_benchmark(path_lexically_normal src/path_lexically_normal.cpp) -add_benchmark(print src/print.cpp) add_benchmark(priority_queue_push_range src/priority_queue_push_range.cpp) add_benchmark(random_integer_generation src/random_integer_generation.cpp) add_benchmark(replace src/replace.cpp) diff --git a/benchmarks/src/print.cpp b/benchmarks/src/efficient_nonlocking_print.cpp similarity index 100% rename from benchmarks/src/print.cpp rename to benchmarks/src/efficient_nonlocking_print.cpp From 3143b1cc006f68158cc54bea6fe5b529fec4a7d7 Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Thu, 29 Aug 2024 20:03:05 +0200 Subject: [PATCH 26/58] Consistent naming --- stl/inc/__msvc_formatter.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stl/inc/__msvc_formatter.hpp b/stl/inc/__msvc_formatter.hpp index 530be1ec79..c13d568c1d 100644 --- a/stl/inc/__msvc_formatter.hpp +++ b/stl/inc/__msvc_formatter.hpp @@ -174,15 +174,15 @@ struct _Formatter_base { _FMT_P2286_END #if _HAS_CXX23 -#define _FORMAT_SPECIALIZE_NON_LOCKING_FOR(_Type) \ +#define _FORMAT_SPECIALIZE_NONLOCKING_FOR(_Type) \ template <> \ inline constexpr bool enable_nonlocking_formatter_optimization<_Type> = true; #else // ^^^ _HAS_CXX23 / !_HAS_CXX23 vvv -#define _FORMAT_SPECIALIZE_NON_LOCKING_FOR(_Type) +#define _FORMAT_SPECIALIZE_NONLOCKING_FOR(_Type) #endif // ^^^ !_HAS_CXX23 ^^^ #define _FORMAT_SPECIALIZE_FOR(_Type, _ArgType) \ - _FORMAT_SPECIALIZE_NON_LOCKING_FOR(_Type); \ + _FORMAT_SPECIALIZE_NONLOCKING_FOR(_Type); \ template <_Format_supported_charT _CharT> \ struct formatter<_Type, _CharT> : _Formatter_base<_Type, _CharT, _ArgType> {} @@ -205,7 +205,7 @@ _FORMAT_SPECIALIZE_FOR(signed char, _Basic_format_arg_type::_Int_type); _FORMAT_SPECIALIZE_FOR(unsigned char, _Basic_format_arg_type::_UInt_type); #undef _FORMAT_SPECIALIZE_FOR -#undef _FORMAT_SPECIALIZE_NON_LOCKING_FOR +#undef _FORMAT_SPECIALIZE_NONLOCKING_FOR // not using the macro because we'd like to add 'set_debug_format' member function in C++23 mode template <_Format_supported_charT _CharT> From 140cd7d88d487d1f039d1dbee69f623d3106bce4 Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Thu, 29 Aug 2024 20:04:58 +0200 Subject: [PATCH 27/58] Remove unecessary _STD qualifiers --- stl/inc/__msvc_formatter.hpp | 4 ++-- stl/inc/chrono | 4 ++-- stl/inc/print | 5 ++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/stl/inc/__msvc_formatter.hpp b/stl/inc/__msvc_formatter.hpp index c13d568c1d..15bf2f82e1 100644 --- a/stl/inc/__msvc_formatter.hpp +++ b/stl/inc/__msvc_formatter.hpp @@ -395,11 +395,11 @@ constexpr bool enable_nonlocking_formatter_optimization constexpr bool enable_nonlocking_formatter_optimization> = - _STD enable_nonlocking_formatter_optimization<_Ty1> && _STD enable_nonlocking_formatter_optimization<_Ty2>; + enable_nonlocking_formatter_optimization<_Ty1> && enable_nonlocking_formatter_optimization<_Ty2>; template constexpr bool enable_nonlocking_formatter_optimization> = - (_STD enable_nonlocking_formatter_optimization<_Ts> && ...); + (enable_nonlocking_formatter_optimization<_Ts> && ...); template <_RANGES input_range _Rng> requires (format_kind<_Rng> != range_format::disabled) diff --git a/stl/inc/chrono b/stl/inc/chrono index e2c67052a5..3b7947e608 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5969,10 +5969,10 @@ struct formatter<_CHRONO zoned_time<_Duration, _TimeZonePtr>, _CharT> #if _HAS_CXX23 template constexpr bool enable_nonlocking_formatter_optimization<_CHRONO duration<_Rep, _Period>> = - _STD enable_nonlocking_formatter_optimization<_Rep>; + enable_nonlocking_formatter_optimization<_Rep>; template -constexpr bool enable_nonlocking_formatter_optimization<_CHRONO zoned_time<_Duration, const std::chrono::time_zone*>> = +constexpr bool enable_nonlocking_formatter_optimization<_CHRONO zoned_time<_Duration, const _CHRONO time_zone*>> = true; #endif diff --git a/stl/inc/print b/stl/inc/print index 5bb9e0022c..0bd41a5bf5 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -252,9 +252,8 @@ inline void _Vprint_unicode_impl( template void _Print_impl( const _Add_newline _Add_nl, FILE* const _Stream, const format_string<_Types...> _Fmt, _Types&&... _Args) { - constexpr bool _Has_format_args = sizeof...(_Types) > 0; - constexpr bool _Print_nonlocking = - (_STD enable_nonlocking_formatter_optimization<_STD remove_cvref_t<_Types>> && ...); + constexpr bool _Has_format_args = sizeof...(_Types) > 0; + constexpr bool _Print_nonlocking = (enable_nonlocking_formatter_optimization> && ...); if constexpr (_Has_format_args) { if constexpr (_STD _Is_ordinary_literal_encoding_utf8()) { From 757d849efd2f2df5a5df805a7331353d8bba87f8 Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Thu, 29 Aug 2024 20:05:40 +0200 Subject: [PATCH 28/58] Add explicit includes --- benchmarks/src/efficient_nonlocking_print.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/benchmarks/src/efficient_nonlocking_print.cpp b/benchmarks/src/efficient_nonlocking_print.cpp index 27aa8aa721..d694b2933e 100644 --- a/benchmarks/src/efficient_nonlocking_print.cpp +++ b/benchmarks/src/efficient_nonlocking_print.cpp @@ -1,5 +1,10 @@ #include +#include +#include #include +#include +#include +#include namespace { using PrintType = void (*)(FILE*, std::string_view, std::format_args); From 24fc31e41c7d18cdff048f21a03ba86e4882f31e Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Thu, 29 Aug 2024 20:06:23 +0200 Subject: [PATCH 29/58] Delete copy functions (and therefore move) for RAII type --- stl/inc/print | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stl/inc/print b/stl/inc/print index 0bd41a5bf5..ccb8eb8d9d 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -31,6 +31,10 @@ struct _NODISCARD _Stream_lock_guard { ~_Stream_lock_guard() { _CSTD _unlock_file(_Stream); } + + _Stream_lock_guard(const _Stream_lock_guard&) = delete; + _Stream_lock_guard& operator=(const _Stream_lock_guard&) = delete; + FILE* const _Stream; }; From 1cf319122c1412ac88f80160f5094e5bb339863a Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Thu, 29 Aug 2024 20:06:53 +0200 Subject: [PATCH 30/58] Don't reuse names is constructors --- stl/inc/print | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/print b/stl/inc/print index ccb8eb8d9d..79b56e803c 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -46,7 +46,7 @@ public: using pointer = void; using reference = void; - explicit _Print_to_stream_it(FILE* const _Stream) noexcept : _Stream(_Stream) {} + explicit _Print_to_stream_it(FILE* const _Stream_) noexcept : _Stream(_Stream_) {} _Print_to_stream_it& operator=(const char) { return *this; @@ -97,8 +97,8 @@ public: using pointer = void; using reference = void; - explicit _Print_to_unicode_console_it(const __std_unicode_console_handle _Console_handle) noexcept - : _Console_handle(_Console_handle) {} + explicit _Print_to_unicode_console_it(const __std_unicode_console_handle _Console_handle_) noexcept + : _Console_handle(_Console_handle_) {} _Print_to_unicode_console_it& operator=(const char) { return *this; From 55cd1e03a801dd376175bdd523b4d08d5262a55f Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Thu, 29 Aug 2024 20:08:27 +0200 Subject: [PATCH 31/58] Remove redundant comments --- stl/inc/print | 2 -- 1 file changed, 2 deletions(-) diff --git a/stl/inc/print b/stl/inc/print index 79b56e803c..459e113d09 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -72,7 +72,6 @@ private: FILE* _Stream; }; -// Print nonunicode _Str to _Stream, caller must have locked _Stream inline void _Print_noformat_nonunicode_nonlocking(FILE* const _Stream, const string_view _Str) { const bool _Was_write_successful = _CSTD _fwrite_nolock(_Str.data(), 1, _Str.size(), _Stream) == _Str.size(); @@ -124,7 +123,6 @@ private: __std_unicode_console_handle _Console_handle; }; -// Print unicode _Str to unicode console handle, caller must have locked _Stream inline void _Print_noformat_unicode_to_console_nonlocking( const __std_unicode_console_handle _Console_handle, const string_view _Str) { const __std_win_error _Console_print_result = From 594bc151a2237466f31650ad6da268a05ab73207 Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Thu, 29 Aug 2024 20:16:43 +0200 Subject: [PATCH 32/58] Fix formatting --- stl/inc/__msvc_formatter.hpp | 4 ++-- stl/inc/chrono | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/stl/inc/__msvc_formatter.hpp b/stl/inc/__msvc_formatter.hpp index 15bf2f82e1..050f0d0c49 100644 --- a/stl/inc/__msvc_formatter.hpp +++ b/stl/inc/__msvc_formatter.hpp @@ -175,14 +175,14 @@ _FMT_P2286_END #if _HAS_CXX23 #define _FORMAT_SPECIALIZE_NONLOCKING_FOR(_Type) \ - template <> \ + template <> \ inline constexpr bool enable_nonlocking_formatter_optimization<_Type> = true; #else // ^^^ _HAS_CXX23 / !_HAS_CXX23 vvv #define _FORMAT_SPECIALIZE_NONLOCKING_FOR(_Type) #endif // ^^^ !_HAS_CXX23 ^^^ #define _FORMAT_SPECIALIZE_FOR(_Type, _ArgType) \ - _FORMAT_SPECIALIZE_NONLOCKING_FOR(_Type); \ + _FORMAT_SPECIALIZE_NONLOCKING_FOR(_Type); \ template <_Format_supported_charT _CharT> \ struct formatter<_Type, _CharT> : _Formatter_base<_Type, _CharT, _ArgType> {} diff --git a/stl/inc/chrono b/stl/inc/chrono index 3b7947e608..2c279af03f 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5972,8 +5972,7 @@ constexpr bool enable_nonlocking_formatter_optimization<_CHRONO duration<_Rep, _ enable_nonlocking_formatter_optimization<_Rep>; template -constexpr bool enable_nonlocking_formatter_optimization<_CHRONO zoned_time<_Duration, const _CHRONO time_zone*>> = - true; +constexpr bool enable_nonlocking_formatter_optimization<_CHRONO zoned_time<_Duration, const _CHRONO time_zone*>> = true; #endif namespace chrono { From c400675b05eefdb0aa570f1281477e6b9bb4a92a Mon Sep 17 00:00:00 2001 From: blackninja9939 Date: Thu, 29 Aug 2024 20:53:52 +0200 Subject: [PATCH 33/58] Fix brace error and comment --- stl/src/print.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/src/print.cpp b/stl/src/print.cpp index 35e6eb9345..88bb86db83 100644 --- a/stl/src/print.cpp +++ b/stl/src/print.cpp @@ -82,11 +82,11 @@ namespace { _Str.~_Heap_string(); } - ::new (&_Str) _Heap_string(_Wide_str{_malloc_crt_t(wchar_t, _Capacity)}; + ::new (&_Str) _Heap_string(_malloc_crt_t(wchar_t, _Capacity)); if (!_Str) [[unlikely]] { _Str_capacity = _Buffer_size; - _Buffer[0] = L'\0'; // Activiate inline buffer member + _Buffer[0] = L'\0'; // Activate inline buffer member return false; } From bc5e9eea50c39c230d4d7cbb45021243e3915b4a Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 29 Aug 2024 22:48:30 -0700 Subject: [PATCH 34/58] Teach `std::copy` to handle move-only output iterators --- stl/inc/format | 2 +- stl/inc/xutility | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/format b/stl/inc/format index 0d394e0317..265940b4c9 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -2352,7 +2352,7 @@ template struct _Fmt_iterator_flush { template static _OutputIt _Flush(const _Ty* const _First, const _Ty* const _Last, _OutputIt _Output) { - return _STD _Copy_unchecked(_First, _Last, _STD move(_Output)); + return _STD copy(_First, _Last, _STD move(_Output)); } }; diff --git a/stl/inc/xutility b/stl/inc/xutility index e223096dee..c96aed4ed7 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4757,8 +4757,8 @@ _CONSTEXPR20 _OutIt copy(_InIt _First, _InIt _Last, _OutIt _Dest) { // copy [_Fi _STD _Adl_verify_range(_First, _Last); const auto _UFirst = _STD _Get_unwrapped(_First); const auto _ULast = _STD _Get_unwrapped(_Last); - const auto _UDest = _STD _Get_unwrapped_n(_Dest, _STD _Idl_distance<_InIt>(_UFirst, _ULast)); - _STD _Seek_wrapped(_Dest, _STD _Copy_unchecked(_UFirst, _ULast, _UDest)); + auto _UDest = _STD _Get_unwrapped_n(_STD move(_Dest), _STD _Idl_distance<_InIt>(_UFirst, _ULast)); + _STD _Seek_wrapped(_Dest, _STD _Copy_unchecked(_UFirst, _ULast, _STD move(_UDest))); return _Dest; } From 39a5922ca9afeaa2bb71cfd68c3dbf4ba23c30e3 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 29 Aug 2024 22:52:35 -0700 Subject: [PATCH 35/58] Remove unnecessary `e_n_f_o` specialization for `input_range`s This only exists to counter the effect of the blanket wording. (Don't ask me why the Standard doesn't simply specify that there is no such specialization :shrug:) --- stl/inc/__msvc_formatter.hpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/stl/inc/__msvc_formatter.hpp b/stl/inc/__msvc_formatter.hpp index 050f0d0c49..32ebf5330e 100644 --- a/stl/inc/__msvc_formatter.hpp +++ b/stl/inc/__msvc_formatter.hpp @@ -400,10 +400,6 @@ constexpr bool enable_nonlocking_formatter_optimization> = template constexpr bool enable_nonlocking_formatter_optimization> = (enable_nonlocking_formatter_optimization<_Ts> && ...); - -template <_RANGES input_range _Rng> - requires (format_kind<_Rng> != range_format::disabled) -constexpr bool enable_nonlocking_formatter_optimization<_Rng> = false; #endif // _HAS_CXX23 _STD_END From 069dbb8075f20d8d28dc7e061eceb2627854949e Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 29 Aug 2024 22:55:20 -0700 Subject: [PATCH 36/58] Removed bodies of "fake" iterator member functions These need only look like iterators to pass concepts. Drive-by: reorder iterator nested types cannonically --- stl/inc/print | 42 ++++++++++-------------------------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/stl/inc/print b/stl/inc/print index 459e113d09..af0d8e9ed2 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -41,28 +41,17 @@ struct _NODISCARD _Stream_lock_guard { class _Print_to_stream_it { public: using iterator_category = output_iterator_tag; - using difference_type = ptrdiff_t; using value_type = void; + using difference_type = ptrdiff_t; using pointer = void; using reference = void; explicit _Print_to_stream_it(FILE* const _Stream_) noexcept : _Stream(_Stream_) {} - _Print_to_stream_it& operator=(const char) { - return *this; - } - - _Print_to_stream_it& operator*() noexcept { - return *this; - } - - _Print_to_stream_it& operator++() noexcept { - return *this; - } - - _Print_to_stream_it operator++(int) noexcept { - return *this; - } + _Print_to_stream_it& operator=(const char); // These members are intentionally not defined + _Print_to_stream_it& operator*(); + _Print_to_stream_it& operator++(); + _Print_to_stream_it operator++(int); FILE* _Get_stream() const noexcept { return _Stream; @@ -91,29 +80,18 @@ struct _Fmt_iterator_flush<_Print_to_stream_it> { class _Print_to_unicode_console_it { public: using iterator_category = output_iterator_tag; - using difference_type = ptrdiff_t; using value_type = void; + using difference_type = ptrdiff_t; using pointer = void; using reference = void; explicit _Print_to_unicode_console_it(const __std_unicode_console_handle _Console_handle_) noexcept : _Console_handle(_Console_handle_) {} - _Print_to_unicode_console_it& operator=(const char) { - return *this; - } - - _Print_to_unicode_console_it& operator*() noexcept { - return *this; - } - - _Print_to_unicode_console_it& operator++() noexcept { - return *this; - } - - _Print_to_unicode_console_it operator++(int) noexcept { - return *this; - } + _Print_to_unicode_console_it& operator=(const char); // These members are intentionally not defined + _Print_to_unicode_console_it& operator*(); + _Print_to_unicode_console_it& operator++(); + _Print_to_unicode_console_it operator++(int); __std_unicode_console_handle _Get_console_handle() const noexcept { return _Console_handle; From 3bcb0a10d8387550bea9121e073e26e9ca6bf727 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 29 Aug 2024 22:57:07 -0700 Subject: [PATCH 37/58] Minimally initialize `_Allocated_string:_Buffer` The mem-initializer `_Buffer(L'\0')` implicitly zeroes the remainder of the array; we need only assign one element to activate the union member. Drive-by: indicate clearly when we're activating union members --- stl/src/print.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/stl/src/print.cpp b/stl/src/print.cpp index 88bb86db83..56a1a4175e 100644 --- a/stl/src/print.cpp +++ b/stl/src/print.cpp @@ -57,7 +57,9 @@ extern "C" { namespace { class _Allocated_string { public: - _Allocated_string() noexcept : _Buffer(L'\0') {} + _Allocated_string() noexcept { + _Buffer[0] = L'\0'; // Activate _Buffer + } ~_Allocated_string() { if (_Using_heap()) { @@ -80,13 +82,16 @@ namespace { if (_Using_heap()) { _Str.~_Heap_string(); + // We must not throw until restoring the invariant that: + // (_Str_capacity == _Buffer_size && _Buffer is active) || + // (_Str_capacity > _Buffer_size && _Str is active) } - ::new (&_Str) _Heap_string(_malloc_crt_t(wchar_t, _Capacity)); + ::new (&_Str) _Heap_string(_malloc_crt_t(wchar_t, _Capacity)); // Activate _Str if (!_Str) [[unlikely]] { _Str_capacity = _Buffer_size; - _Buffer[0] = L'\0'; // Activate inline buffer member + _Buffer[0] = L'\0'; // Activate _Buffer return false; } From 05dc4c6f35352cf17aafea2a571e6681df28b79f Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Thu, 29 Aug 2024 23:01:06 -0700 Subject: [PATCH 38/58] Add missing `e_n_f_o` specializations ... and a test to ensure they're working as required. I've omitted some the required specializations for `priority_queue`, `queue`, and `stack` due to confusion about exactly what the wording means. --- stl/inc/chrono | 137 +++++++++++++++- stl/inc/stacktrace | 6 + stl/inc/thread | 3 + stl/inc/vector | 4 + tests/std/test.lst | 1 + .../P3107R5_enabled_specializations/env.lst | 4 + .../test.compile.pass.cpp | 149 ++++++++++++++++++ 7 files changed, 299 insertions(+), 5 deletions(-) create mode 100644 tests/std/tests/P3107R5_enabled_specializations/env.lst create mode 100644 tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp diff --git a/stl/inc/chrono b/stl/inc/chrono index 2c279af03f..8b697332c0 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5805,62 +5805,158 @@ template struct formatter<_CHRONO duration<_Rep, _Period>, _CharT> : _Fill_tm_formatter<_CHRONO duration<_Rep, _Period>, _CharT> {}; +#if _HAS_CXX23 +template +constexpr bool enable_nonlocking_formatter_optimization<_CHRONO duration<_Rep, _Period>> = + enable_nonlocking_formatter_optimization<_Rep>; +#endif // _HAS_CXX23 + template <_Format_supported_charT _CharT> struct formatter<_CHRONO day, _CharT> : _Fill_tm_formatter<_CHRONO day, _CharT> {}; +#if _HAS_CXX23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO day> = true; +#endif // _HAS_CXX23 + template <_Format_supported_charT _CharT> struct formatter<_CHRONO month, _CharT> : _Fill_tm_formatter<_CHRONO month, _CharT> {}; +#if _HAS_CXX23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO month> = true; +#endif // _HAS_CXX23 + template <_Format_supported_charT _CharT> struct formatter<_CHRONO year, _CharT> : _Fill_tm_formatter<_CHRONO year, _CharT> {}; +#if _HAS_CXX23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO year> = true; +#endif // _HAS_CXX23 + template <_Format_supported_charT _CharT> struct formatter<_CHRONO weekday, _CharT> : _Fill_tm_formatter<_CHRONO weekday, _CharT> {}; +#if _HAS_CXX23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO weekday> = true; +#endif // _HAS_CXX23 + template <_Format_supported_charT _CharT> struct formatter<_CHRONO weekday_indexed, _CharT> : _Fill_tm_formatter<_CHRONO weekday_indexed, _CharT> {}; +#if _HAS_CXX23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO weekday_indexed> = true; +#endif // _HAS_CXX23 + template <_Format_supported_charT _CharT> struct formatter<_CHRONO weekday_last, _CharT> : _Fill_tm_formatter<_CHRONO weekday_last, _CharT> {}; +#if _HAS_CXX23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO weekday_last> = true; +#endif // _HAS_CXX23 + template <_Format_supported_charT _CharT> struct formatter<_CHRONO month_day, _CharT> : _Fill_tm_formatter<_CHRONO month_day, _CharT> {}; +#if _HAS_CXX23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO month_day> = true; +#endif // _HAS_CXX23 + template <_Format_supported_charT _CharT> struct formatter<_CHRONO month_day_last, _CharT> : _Fill_tm_formatter<_CHRONO month_day_last, _CharT> {}; +#if _HAS_CXX23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO month_day_last> = true; +#endif // _HAS_CXX23 + template <_Format_supported_charT _CharT> struct formatter<_CHRONO month_weekday, _CharT> : _Fill_tm_formatter<_CHRONO month_weekday, _CharT> {}; +#if _HAS_CXX23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO month_weekday> = true; +#endif // _HAS_CXX23 + template <_Format_supported_charT _CharT> struct formatter<_CHRONO month_weekday_last, _CharT> : _Fill_tm_formatter<_CHRONO month_weekday_last, _CharT> {}; +#if _HAS_CXX23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO month_weekday_last> = true; +#endif // _HAS_CXX23 + template <_Format_supported_charT _CharT> struct formatter<_CHRONO year_month, _CharT> : _Fill_tm_formatter<_CHRONO year_month, _CharT> {}; +#if _HAS_CXX23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO year_month> = true; +#endif // _HAS_CXX23 + template <_Format_supported_charT _CharT> struct formatter<_CHRONO year_month_day, _CharT> : _Fill_tm_formatter<_CHRONO year_month_day, _CharT> {}; +#if _HAS_CXX23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO year_month_day> = true; +#endif // _HAS_CXX23 + template <_Format_supported_charT _CharT> struct formatter<_CHRONO year_month_day_last, _CharT> : _Fill_tm_formatter<_CHRONO year_month_day_last, _CharT> {}; +#if _HAS_CXX23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO year_month_day_last> = true; +#endif // _HAS_CXX23 + template <_Format_supported_charT _CharT> struct formatter<_CHRONO year_month_weekday, _CharT> : _Fill_tm_formatter<_CHRONO year_month_weekday, _CharT> {}; +#if _HAS_CXX23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO year_month_weekday> = true; +#endif // _HAS_CXX23 + template <_Format_supported_charT _CharT> struct formatter<_CHRONO year_month_weekday_last, _CharT> : _Fill_tm_formatter<_CHRONO year_month_weekday_last, _CharT> {}; +#if _HAS_CXX23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO year_month_weekday_last> = true; +#endif // _HAS_CXX23 + template struct formatter<_CHRONO hh_mm_ss<_CHRONO duration<_Rep, _Period>>, _CharT> : _Fill_tm_formatter<_CHRONO hh_mm_ss<_CHRONO duration<_Rep, _Period>>, _CharT> {}; +#if _HAS_CXX23 +template +constexpr bool enable_nonlocking_formatter_optimization<_CHRONO hh_mm_ss<_CHRONO duration<_Rep, _Period>>> = true; +#endif // _HAS_CXX23 + template <_Format_supported_charT _CharT> struct formatter<_CHRONO sys_info, _CharT> : _Fill_tm_formatter<_CHRONO sys_info, _CharT> {}; +#if _HAS_CXX23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO sys_info> = true; +#endif // _HAS_CXX23 + template <_Format_supported_charT _CharT> struct formatter<_CHRONO local_info, _CharT> : _Fill_tm_formatter<_CHRONO local_info, _CharT> {}; +#if _HAS_CXX23 +template <> +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO local_info> = true; +#endif // _HAS_CXX23 + template struct formatter<_CHRONO sys_time<_Duration>, _CharT> { constexpr auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { @@ -5876,6 +5972,11 @@ private: _CHRONO _Chrono_formatter<_CharT> _Impl{_STATICALLY_WIDEN(_CharT, "UTC")}; }; +#if _HAS_CXX23 +template +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO sys_time<_Duration>> = true; +#endif // _HAS_CXX23 + template struct formatter<_CHRONO utc_time<_Duration>, _CharT> { constexpr auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { @@ -5892,6 +5993,11 @@ private: _CHRONO _Chrono_formatter<_CharT> _Impl{_STATICALLY_WIDEN(_CharT, "UTC")}; }; +#if _HAS_CXX23 +template +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO utc_time<_Duration>> = true; +#endif // _HAS_CXX23 + template struct formatter<_CHRONO tai_time<_Duration>, _CharT> { constexpr auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { @@ -5911,6 +6017,11 @@ private: _CHRONO _Chrono_formatter<_CharT> _Impl{_STATICALLY_WIDEN(_CharT, "TAI")}; }; +#if _HAS_CXX23 +template +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO tai_time<_Duration>> = true; +#endif // _HAS_CXX23 + template struct formatter<_CHRONO gps_time<_Duration>, _CharT> { constexpr auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { @@ -5930,6 +6041,11 @@ private: _CHRONO _Chrono_formatter<_CharT> _Impl{_STATICALLY_WIDEN(_CharT, "GPS")}; }; +#if _HAS_CXX23 +template +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO gps_time<_Duration>> = true; +#endif // _HAS_CXX23 + template struct formatter<_CHRONO file_time<_Duration>, _CharT> { constexpr auto parse(basic_format_parse_context<_CharT>& _Parse_ctx) { @@ -5947,13 +6063,28 @@ private: _CHRONO _Chrono_formatter<_CharT> _Impl{_STATICALLY_WIDEN(_CharT, "UTC")}; }; +#if _HAS_CXX23 +template +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO file_time<_Duration>> = true; +#endif // _HAS_CXX23 + template struct formatter<_CHRONO local_time<_Duration>, _CharT> : _Fill_tm_formatter<_CHRONO local_time<_Duration>, _CharT> {}; +#if _HAS_CXX23 +template +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO local_time<_Duration>> = true; +#endif // _HAS_CXX23 + template struct formatter<_CHRONO _Local_time_format_t<_Duration>, _CharT> : _Fill_tm_formatter<_CHRONO _Local_time_format_t<_Duration>, _CharT> {}; +#if _HAS_CXX23 +template +inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO _Local_time_format_t<_Duration>> = true; +#endif // _HAS_CXX23 + template struct formatter<_CHRONO zoned_time<_Duration, _TimeZonePtr>, _CharT> : formatter<_CHRONO _Local_time_format_t<_Duration>, _CharT> { @@ -5967,13 +6098,9 @@ struct formatter<_CHRONO zoned_time<_Duration, _TimeZonePtr>, _CharT> }; #if _HAS_CXX23 -template -constexpr bool enable_nonlocking_formatter_optimization<_CHRONO duration<_Rep, _Period>> = - enable_nonlocking_formatter_optimization<_Rep>; - template constexpr bool enable_nonlocking_formatter_optimization<_CHRONO zoned_time<_Duration, const _CHRONO time_zone*>> = true; -#endif +#endif // _HAS_CXX23 namespace chrono { template diff --git a/stl/inc/stacktrace b/stl/inc/stacktrace index b5b57c00c9..0bcea0568d 100644 --- a/stl/inc/stacktrace +++ b/stl/inc/stacktrace @@ -356,6 +356,9 @@ private: _Fill_align_and_width_formatter _Impl; }; +template <> +inline constexpr bool enable_nonlocking_formatter_optimization = true; + template struct formatter> { constexpr format_parse_context::iterator parse(format_parse_context& _Parse_ctx) { @@ -373,6 +376,9 @@ struct formatter> { } }; +template +inline constexpr bool enable_nonlocking_formatter_optimization> = true; + namespace pmr { _EXPORT_STD using stacktrace = basic_stacktrace>; } diff --git a/stl/inc/thread b/stl/inc/thread index 079f4b7af3..3fd0031805 100644 --- a/stl/inc/thread +++ b/stl/inc/thread @@ -330,6 +330,9 @@ public: private: _Fill_align_and_width_formatter<_CharT> _Impl; }; + +template <> +inline constexpr bool enable_nonlocking_formatter_optimization = true; #endif // _HAS_CXX23 template <> diff --git a/stl/inc/vector b/stl/inc/vector index 141068db91..f682d41fee 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -3595,6 +3595,10 @@ public: return _Underlying.format(_Ref, _Ctx); } }; + +template + requires _Is_specialization_v<_Ty, _Vb_reference> +constexpr bool enable_nonlocking_formatter_optimization<_Ty> = true; #endif // _HAS_CXX23 template diff --git a/tests/std/test.lst b/tests/std/test.lst index aed8328076..fde8f2a1d2 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -678,6 +678,7 @@ 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\P3107R5_enabled_specializations tests\VSO_0000000_allocator_propagation tests\VSO_0000000_any_calling_conventions tests\VSO_0000000_c_math_functions diff --git a/tests/std/tests/P3107R5_enabled_specializations/env.lst b/tests/std/tests/P3107R5_enabled_specializations/env.lst new file mode 100644 index 0000000000..642f530ffa --- /dev/null +++ b/tests/std/tests/P3107R5_enabled_specializations/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp b/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp new file mode 100644 index 0000000000..473d7a1ea0 --- /dev/null +++ b/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp @@ -0,0 +1,149 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +struct unoptimized {}; + +template +struct std::formatter : formatter { + template + auto format(const unoptimized&, FormatContext& ctx) { + return formatter::format("unoptimized", ctx); + } +}; + +static_assert(!enable_nonlocking_formatter_optimization); + +template +struct myalloc { + using value_type = T; + + template + myalloc(const myalloc&); + + T* allocate(size_t); + void deallocate(T*, size_t) noexcept; + + template + bool operator==(const myalloc&) const; +}; + +// ==================================================================== +static_assert(enable_nonlocking_formatter_optimization); +static_assert(!enable_nonlocking_formatter_optimization>); + +static_assert(enable_nonlocking_formatter_optimization>); +static_assert(!enable_nonlocking_formatter_optimization>>); +static_assert(enable_nonlocking_formatter_optimization>); +static_assert(!enable_nonlocking_formatter_optimization>>); +static_assert(enable_nonlocking_formatter_optimization>); +static_assert(!enable_nonlocking_formatter_optimization>>); +static_assert(enable_nonlocking_formatter_optimization>); +static_assert(!enable_nonlocking_formatter_optimization>>); +static_assert(enable_nonlocking_formatter_optimization>); +static_assert(!enable_nonlocking_formatter_optimization>>); +static_assert(enable_nonlocking_formatter_optimization>); +static_assert(!enable_nonlocking_formatter_optimization>>); + +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); + +static_assert(enable_nonlocking_formatter_optimization>); +static_assert(!enable_nonlocking_formatter_optimization>>); + +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); + +static_assert(enable_nonlocking_formatter_optimization>); +static_assert(!enable_nonlocking_formatter_optimization>>); +static_assert(!enable_nonlocking_formatter_optimization>); + +// ==================================================================== +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization, myalloc>>); +static_assert(enable_nonlocking_formatter_optimization, myalloc>>); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization>); +static_assert(!enable_nonlocking_formatter_optimization>); +static_assert(!enable_nonlocking_formatter_optimization>); +static_assert(enable_nonlocking_formatter_optimization>); +static_assert(!enable_nonlocking_formatter_optimization>); +static_assert(!enable_nonlocking_formatter_optimization>); + +// Validate that various ranges are unoptimized +static_assert(!enable_nonlocking_formatter_optimization>); +static_assert(!enable_nonlocking_formatter_optimization>>); +static_assert(!enable_nonlocking_formatter_optimization>); +static_assert(!enable_nonlocking_formatter_optimization>>); +static_assert(!enable_nonlocking_formatter_optimization>); +static_assert(!enable_nonlocking_formatter_optimization>>); +static_assert(!enable_nonlocking_formatter_optimization>); +static_assert(!enable_nonlocking_formatter_optimization>>); +static_assert(!enable_nonlocking_formatter_optimization>); +static_assert(!enable_nonlocking_formatter_optimization>>); +using R = decltype(vector{} | views::take(3)); +static_assert(!enable_nonlocking_formatter_optimization); + +// ================================================================ +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization); +static_assert(enable_nonlocking_formatter_optimization>>); + +// ==================================================================== +static_assert(enable_nonlocking_formatter_optimization); + +// ==================================================================== +static_assert(enable_nonlocking_formatter_optimization::reference>); +static_assert(enable_nonlocking_formatter_optimization>::reference>); From bb52149e64fe7781af59af288e4c8a0154e9618e Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Fri, 30 Aug 2024 11:34:57 -0700 Subject: [PATCH 39/58] Indicate partial implementation of P3235R3 I've filed GH-4924 to followup with LWG; we'll complete the implementation once the wording has been clarified. --- stl/inc/yvals_core.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 6c3520a5ea..91a90259b1 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -394,9 +394,10 @@ // P2833R2 Freestanding Library: inout expected span // (except for __cpp_lib_span which also covers C++26 span::at) // P2836R1 basic_const_iterator Should Follow Its Underlying Type's Convertibility +// P3107R5 Permit An Efficient Implementation Of // P3142R0 Printing Blank Lines With println() -// P3107R5 Permit An Efficient Implementation Of // P3235R3 std::print More Types Faster With Less Memory +// (partial implementation; see GH-4924) // _HAS_CXX23 and _SILENCE_ALL_CXX23_DEPRECATION_WARNINGS control: // P1413R3 Deprecate aligned_storage And aligned_union From 96f861b889506f6263fbcc23451facfb34dc3104 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Fri, 30 Aug 2024 11:45:21 -0700 Subject: [PATCH 40/58] Remove extraneous `inline` from variable template --- stl/inc/stacktrace | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/stacktrace b/stl/inc/stacktrace index 0bcea0568d..c2e109235b 100644 --- a/stl/inc/stacktrace +++ b/stl/inc/stacktrace @@ -377,7 +377,7 @@ struct formatter> { }; template -inline constexpr bool enable_nonlocking_formatter_optimization> = true; +constexpr bool enable_nonlocking_formatter_optimization> = true; namespace pmr { _EXPORT_STD using stacktrace = basic_stacktrace>; From 6b520c2d32fb4ac3a152fb7f8aeb1579de3c6c48 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Fri, 30 Aug 2024 12:27:18 -0700 Subject: [PATCH 41/58] Fix `chrono` `e_n_f_o` tests I adjusted the specializations at the last minute, but didn't make the corresponding test changes. --- .../test.compile.pass.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp b/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp index 473d7a1ea0..35d80261f5 100644 --- a/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp +++ b/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp @@ -45,17 +45,17 @@ static_assert(enable_nonlocking_formatter_optimization); static_assert(!enable_nonlocking_formatter_optimization>); static_assert(enable_nonlocking_formatter_optimization>); -static_assert(!enable_nonlocking_formatter_optimization>>); +static_assert(enable_nonlocking_formatter_optimization>>); static_assert(enable_nonlocking_formatter_optimization>); -static_assert(!enable_nonlocking_formatter_optimization>>); +static_assert(enable_nonlocking_formatter_optimization>>); static_assert(enable_nonlocking_formatter_optimization>); -static_assert(!enable_nonlocking_formatter_optimization>>); +static_assert(enable_nonlocking_formatter_optimization>>); static_assert(enable_nonlocking_formatter_optimization>); -static_assert(!enable_nonlocking_formatter_optimization>>); +static_assert(enable_nonlocking_formatter_optimization>>); static_assert(enable_nonlocking_formatter_optimization>); -static_assert(!enable_nonlocking_formatter_optimization>>); +static_assert(enable_nonlocking_formatter_optimization>>); static_assert(enable_nonlocking_formatter_optimization>); -static_assert(!enable_nonlocking_formatter_optimization>>); +static_assert(enable_nonlocking_formatter_optimization>>); static_assert(enable_nonlocking_formatter_optimization); static_assert(enable_nonlocking_formatter_optimization); @@ -74,13 +74,13 @@ static_assert(enable_nonlocking_formatter_optimization); static_assert(enable_nonlocking_formatter_optimization>); -static_assert(!enable_nonlocking_formatter_optimization>>); +static_assert(enable_nonlocking_formatter_optimization>>); static_assert(enable_nonlocking_formatter_optimization); static_assert(enable_nonlocking_formatter_optimization); static_assert(enable_nonlocking_formatter_optimization>); -static_assert(!enable_nonlocking_formatter_optimization>>); +static_assert(enable_nonlocking_formatter_optimization>>); static_assert(!enable_nonlocking_formatter_optimization>); // ==================================================================== From 1059e5ca399d1093921f00b6303f50f38dcf5c82 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Aug 2024 13:36:58 -0700 Subject: [PATCH 42/58] Add license banner. --- benchmarks/src/efficient_nonlocking_print.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/benchmarks/src/efficient_nonlocking_print.cpp b/benchmarks/src/efficient_nonlocking_print.cpp index d694b2933e..aacaee87c9 100644 --- a/benchmarks/src/efficient_nonlocking_print.cpp +++ b/benchmarks/src/efficient_nonlocking_print.cpp @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + #include #include #include From b83bd1200614b776beb2c21f76b29541c1295700 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Aug 2024 13:37:48 -0700 Subject: [PATCH 43/58] Add newline. --- .../tests/P3107R5_enabled_specializations/test.compile.pass.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp b/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp index 35d80261f5..eafb3e195f 100644 --- a/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp +++ b/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + #include #include #include From a94e21ff4b20d447750af0b1f645647852591a3c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Aug 2024 13:42:25 -0700 Subject: [PATCH 44/58] format should be a const member function. --- .../tests/P3107R5_enabled_specializations/test.compile.pass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp b/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp index eafb3e195f..ecae68a933 100644 --- a/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp +++ b/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp @@ -20,7 +20,7 @@ struct unoptimized {}; template struct std::formatter : formatter { template - auto format(const unoptimized&, FormatContext& ctx) { + auto format(const unoptimized&, FormatContext& ctx) const { return formatter::format("unoptimized", ctx); } }; From d20ec48efa911a72c687ebdfcc8b247bc1b141b8 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Aug 2024 13:42:48 -0700 Subject: [PATCH 45/58] Verify that unoptimized is formattable. --- .../tests/P3107R5_enabled_specializations/test.compile.pass.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp b/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp index ecae68a933..627b99f6e8 100644 --- a/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp +++ b/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp @@ -25,6 +25,7 @@ struct std::formatter : formatter { } }; +static_assert(formattable); static_assert(!enable_nonlocking_formatter_optimization); template From 7af30288fadf3b3d2b18907d9eeac85739e7af94 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Aug 2024 13:43:50 -0700 Subject: [PATCH 46/58] Include cstddef for size_t. --- .../tests/P3107R5_enabled_specializations/test.compile.pass.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp b/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp index 627b99f6e8..19bd843f0e 100644 --- a/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp +++ b/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include +#include #include #include #include From 3d69e3c303a1887caa9a18e14673242dd64d4224 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Aug 2024 13:52:05 -0700 Subject: [PATCH 47/58] Test chrono::local-time-format-t. --- .../tests/P3107R5_enabled_specializations/test.compile.pass.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp b/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp index 19bd843f0e..05b905217b 100644 --- a/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp +++ b/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp @@ -59,6 +59,7 @@ static_assert(enable_nonlocking_formatter_optimization>>); static_assert(enable_nonlocking_formatter_optimization>); static_assert(enable_nonlocking_formatter_optimization>>); +static_assert(enable_nonlocking_formatter_optimization); static_assert(enable_nonlocking_formatter_optimization); static_assert(enable_nonlocking_formatter_optimization); From f2cecfd82a28d94540b4ec2f622c1acb824a4ac3 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Aug 2024 13:54:09 -0700 Subject: [PATCH 48/58] Fix comment: 202406L is actually for P3235R3. --- tests/libcxx/expected_results.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 9e33b6b4f1..7745dde54c 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -85,7 +85,7 @@ std/language.support/support.limits/support.limits.general/algorithm.version.com std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp FAIL std/language.support/support.limits/support.limits.general/iterator.version.compile.pass.cpp FAIL -# Test expects __cpp_lib_print to have the old value 202207L for P2093R14; we define the C++23 value 202406L for P3107R5. +# Test expects __cpp_lib_print to have the old value 202207L for P2093R14; we define the C++23 value 202406L for P3235R3. std/language.support/support.limits/support.limits.general/ostream.version.compile.pass.cpp FAIL std/language.support/support.limits/support.limits.general/print.version.compile.pass.cpp FAIL From 1380ac878c0e66855f07dc4c7786fba3e2832b7c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Aug 2024 14:05:30 -0700 Subject: [PATCH 49/58] Recategorize libcxx failure. --- tests/libcxx/expected_results.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 7745dde54c..356c311b03 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -240,9 +240,6 @@ std/language.support/support.limits/support.limits.general/cstdlib.version.compi # P2255R2 "Type Traits To Detect References Binding To Temporaries" std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp FAIL -# P3107R5 "Permit An Efficient Implementation Of " -std/utilities/format/format.formatter/format.formatter.locking/enable_nonlocking_formatter_optimization.compile.pass.cpp FAIL - # *** MISSING COMPILER FEATURES *** # P1169R4 static operator() @@ -585,6 +582,9 @@ std/iterators/iterator.requirements/iterator.concepts/iterator.concept.random.ac # Bogus test expects to_address() to SFINAE away for int. std/utilities/memory/pointer.conversion/to_address_without_pointer_traits.pass.cpp FAIL +# We disagree about whether various chrono types should be optimized, and the test is clearly wrong about vector::reference. +std/utilities/format/format.formatter/format.formatter.locking/enable_nonlocking_formatter_optimization.compile.pass.cpp FAIL + # *** LIKELY STL BUGS *** # Not analyzed, likely STL bugs. Various assertions. From fe434ac071bd5e6886058006f3302e8a8bc22e96 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Aug 2024 14:14:57 -0700 Subject: [PATCH 50/58] Drop inline for variable templates. --- stl/inc/chrono | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 8b697332c0..fefe806cd6 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5974,7 +5974,7 @@ private: #if _HAS_CXX23 template -inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO sys_time<_Duration>> = true; +constexpr bool enable_nonlocking_formatter_optimization<_CHRONO sys_time<_Duration>> = true; #endif // _HAS_CXX23 template @@ -5995,7 +5995,7 @@ private: #if _HAS_CXX23 template -inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO utc_time<_Duration>> = true; +constexpr bool enable_nonlocking_formatter_optimization<_CHRONO utc_time<_Duration>> = true; #endif // _HAS_CXX23 template @@ -6019,7 +6019,7 @@ private: #if _HAS_CXX23 template -inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO tai_time<_Duration>> = true; +constexpr bool enable_nonlocking_formatter_optimization<_CHRONO tai_time<_Duration>> = true; #endif // _HAS_CXX23 template @@ -6043,7 +6043,7 @@ private: #if _HAS_CXX23 template -inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO gps_time<_Duration>> = true; +constexpr bool enable_nonlocking_formatter_optimization<_CHRONO gps_time<_Duration>> = true; #endif // _HAS_CXX23 template @@ -6065,7 +6065,7 @@ private: #if _HAS_CXX23 template -inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO file_time<_Duration>> = true; +constexpr bool enable_nonlocking_formatter_optimization<_CHRONO file_time<_Duration>> = true; #endif // _HAS_CXX23 template @@ -6073,7 +6073,7 @@ struct formatter<_CHRONO local_time<_Duration>, _CharT> : _Fill_tm_formatter<_CH #if _HAS_CXX23 template -inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO local_time<_Duration>> = true; +constexpr bool enable_nonlocking_formatter_optimization<_CHRONO local_time<_Duration>> = true; #endif // _HAS_CXX23 template @@ -6082,7 +6082,7 @@ struct formatter<_CHRONO _Local_time_format_t<_Duration>, _CharT> #if _HAS_CXX23 template -inline constexpr bool enable_nonlocking_formatter_optimization<_CHRONO _Local_time_format_t<_Duration>> = true; +constexpr bool enable_nonlocking_formatter_optimization<_CHRONO _Local_time_format_t<_Duration>> = true; #endif // _HAS_CXX23 template From 8dad2827a9951ca0d684fc9c8bf2b03432c15549 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Aug 2024 14:24:33 -0700 Subject: [PATCH 51/58] Avoid emitting an unnecessary semicolon. --- stl/inc/__msvc_formatter.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/__msvc_formatter.hpp b/stl/inc/__msvc_formatter.hpp index 32ebf5330e..ed9d1071ad 100644 --- a/stl/inc/__msvc_formatter.hpp +++ b/stl/inc/__msvc_formatter.hpp @@ -182,7 +182,7 @@ _FMT_P2286_END #endif // ^^^ !_HAS_CXX23 ^^^ #define _FORMAT_SPECIALIZE_FOR(_Type, _ArgType) \ - _FORMAT_SPECIALIZE_NONLOCKING_FOR(_Type); \ + _FORMAT_SPECIALIZE_NONLOCKING_FOR(_Type) \ template <_Format_supported_charT _CharT> \ struct formatter<_Type, _CharT> : _Formatter_base<_Type, _CharT, _ArgType> {} From 1b2d79bd5a66ed4e48bde864a4038048c0cf6dc1 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Aug 2024 14:28:01 -0700 Subject: [PATCH 52/58] Mark `_Allocated_string::_Using_heap` as nodiscard. --- stl/src/print.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/src/print.cpp b/stl/src/print.cpp index 56a1a4175e..61efeec768 100644 --- a/stl/src/print.cpp +++ b/stl/src/print.cpp @@ -106,7 +106,7 @@ namespace { // internal transcoding buffer static constexpr size_t _Buffer_size = 2048; - bool _Using_heap() const noexcept { + [[nodiscard]] bool _Using_heap() const noexcept { return _Str_capacity > _Buffer_size; } From 3951d44e4b42237790fbf9f4a106ca998a456905 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Aug 2024 14:40:09 -0700 Subject: [PATCH 53/58] Drop ignored top-level const for value parameters. --- stl/inc/print | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/print b/stl/inc/print index af0d8e9ed2..74090334a9 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -48,7 +48,7 @@ public: explicit _Print_to_stream_it(FILE* const _Stream_) noexcept : _Stream(_Stream_) {} - _Print_to_stream_it& operator=(const char); // These members are intentionally not defined + _Print_to_stream_it& operator=(char); // These members are intentionally not defined _Print_to_stream_it& operator*(); _Print_to_stream_it& operator++(); _Print_to_stream_it operator++(int); @@ -88,7 +88,7 @@ public: explicit _Print_to_unicode_console_it(const __std_unicode_console_handle _Console_handle_) noexcept : _Console_handle(_Console_handle_) {} - _Print_to_unicode_console_it& operator=(const char); // These members are intentionally not defined + _Print_to_unicode_console_it& operator=(char); // These members are intentionally not defined _Print_to_unicode_console_it& operator*(); _Print_to_unicode_console_it& operator++(); _Print_to_unicode_console_it operator++(int); From 48c1b19f17038805ecae314863a2901f46344331 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Aug 2024 14:54:20 -0700 Subject: [PATCH 54/58] Avoid parameter/data member shadowing. --- stl/inc/print | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/print b/stl/inc/print index 74090334a9..3124fcd680 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -25,7 +25,7 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN struct _NODISCARD _Stream_lock_guard { - explicit _Stream_lock_guard(FILE* const _Stream) : _Stream(_Stream) { + explicit _Stream_lock_guard(FILE* const _Stream_) : _Stream(_Stream_) { _CSTD _lock_file(_Stream); } ~_Stream_lock_guard() { From a10240cad5b4691cb5050fdc475ca28571066bd4 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Aug 2024 15:06:03 -0700 Subject: [PATCH 55/58] Add comma to comment for clarity. --- stl/inc/print | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/print b/stl/inc/print index 3124fcd680..6e6b15b9e8 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -180,7 +180,7 @@ void _Do_on_maybe_unicode_console( } } -// If a unicode console write using native API, else write to stream +// If a unicode console, write using native API, else write to stream inline void _Vprint_unicode_noformat_impl(FILE* const _Stream, const string_view _Output_str) { const auto _Unicode_console = [&](const __std_unicode_console_handle _Console_handle) { const _Stream_lock_guard _Guard{_Stream}; From 48b68979c54efed0f4ba06622beffc4f67733c3f Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Aug 2024 15:29:24 -0700 Subject: [PATCH 56/58] Add missing "to" and rewrap. --- benchmarks/src/efficient_nonlocking_print.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/src/efficient_nonlocking_print.cpp b/benchmarks/src/efficient_nonlocking_print.cpp index aacaee87c9..4576fcee48 100644 --- a/benchmarks/src/efficient_nonlocking_print.cpp +++ b/benchmarks/src/efficient_nonlocking_print.cpp @@ -29,8 +29,8 @@ namespace { const std::pair p{16, 2.073f}; for (auto _ : state) { PrintFunction(stdout, - "Hello cool I am going print as unicode!! {:X}, {}, {:a}, I am a big string, lots of words, multiple " - "{} formats\n", + "Hello cool I am going to print as unicode!! {:X}, {}, {:a}, " + "I am a big string, lots of words, multiple {} formats\n", std::make_format_args(i, str, f, p)); } } From e013b2c50cd662be1470d5a2fddf5fc381d7cb25 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Aug 2024 15:48:58 -0700 Subject: [PATCH 57/58] Add a comment about the `--benchmark_out` options. --- benchmarks/src/efficient_nonlocking_print.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/benchmarks/src/efficient_nonlocking_print.cpp b/benchmarks/src/efficient_nonlocking_print.cpp index 4576fcee48..d14973ddc9 100644 --- a/benchmarks/src/efficient_nonlocking_print.cpp +++ b/benchmarks/src/efficient_nonlocking_print.cpp @@ -1,6 +1,9 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// This benchmark inherently prints many lines to stdout. To view its results, run it with these options: +// --benchmark_out=efficient_nonlocking_print.log --benchmark_out_format=console + #include #include #include From 83596d295bc17609cf6ef0a740fd45a4236bfd95 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Fri, 30 Aug 2024 15:54:04 -0700 Subject: [PATCH 58/58] Add comment about other chrono types that depend on duration. --- .../tests/P3107R5_enabled_specializations/test.compile.pass.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp b/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp index 05b905217b..53c28d0c0e 100644 --- a/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp +++ b/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp @@ -47,6 +47,8 @@ struct myalloc { static_assert(enable_nonlocking_formatter_optimization); static_assert(!enable_nonlocking_formatter_optimization>); +// Other than `duration` itself, other types that depend on `duration` should be unconditionally +// enabled since they decompose `duration`s rather than formatting them directly. static_assert(enable_nonlocking_formatter_optimization>); static_assert(enable_nonlocking_formatter_optimization>>); static_assert(enable_nonlocking_formatter_optimization>);