diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index 7c98173e06..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) diff --git a/benchmarks/src/efficient_nonlocking_print.cpp b/benchmarks/src/efficient_nonlocking_print.cpp new file mode 100644 index 0000000000..d14973ddc9 --- /dev/null +++ b/benchmarks/src/efficient_nonlocking_print.cpp @@ -0,0 +1,44 @@ +// 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 +#include +#include +#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 I am going to print as 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 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)); + } + } + BENCHMARK(BM_vprint_complex<&std::vprint_unicode>); + BENCHMARK(BM_vprint_complex<&std::vprint_unicode_buffered>); +} // namespace + +BENCHMARK_MAIN(); diff --git a/stl/inc/__msvc_formatter.hpp b/stl/inc/__msvc_formatter.hpp index 5ff1bea2f4..ed9d1071ad 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_NONLOCKING_FOR(_Type) \ + 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) \ 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_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> @@ -361,6 +374,32 @@ struct formatter, _CharT>; template <_Format_supported_charT _CharT, class... _Types> struct formatter, _CharT>; + +template <_Format_supported_charT _CharT> +constexpr bool enable_nonlocking_formatter_optimization<_CharT> = true; + +template <_Format_supported_charT _CharT> +constexpr bool enable_nonlocking_formatter_optimization<_CharT*> = true; + +template <_Format_supported_charT _CharT> +constexpr bool enable_nonlocking_formatter_optimization = true; + +template <_Format_supported_charT _CharT, size_t _Nx> +constexpr bool enable_nonlocking_formatter_optimization<_CharT[_Nx]> = true; + +template <_Format_supported_charT _CharT, class _Traits, class _Allocator> +constexpr bool enable_nonlocking_formatter_optimization> = true; + +template <_Format_supported_charT _CharT, class _Traits> +constexpr bool enable_nonlocking_formatter_optimization> = true; + +template +constexpr bool enable_nonlocking_formatter_optimization> = + enable_nonlocking_formatter_optimization<_Ty1> && enable_nonlocking_formatter_optimization<_Ty2>; + +template +constexpr bool enable_nonlocking_formatter_optimization> = + (enable_nonlocking_formatter_optimization<_Ts> && ...); #endif // _HAS_CXX23 _STD_END diff --git a/stl/inc/chrono b/stl/inc/chrono index 0a2db79cb6..fefe806cd6 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 +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 +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 +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 +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 +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 +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 +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> { @@ -5966,6 +6097,11 @@ struct formatter<_CHRONO zoned_time<_Duration, _TimeZonePtr>, _CharT> } }; +#if _HAS_CXX23 +template +constexpr bool enable_nonlocking_formatter_optimization<_CHRONO zoned_time<_Duration, const _CHRONO time_zone*>> = true; +#endif // _HAS_CXX23 + namespace chrono { template _NODISCARD string nonexistent_local_time::_Make_string(const local_time<_Duration>& _Tp, const local_info& _Info) { diff --git a/stl/inc/format b/stl/inc/format index a53b3164b8..265940b4c9 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_}; @@ -2355,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* const _First, const _Ty* const _Last, _OutputIt _Output) { + return _STD copy(_First, _Last, _STD move(_Output)); + } }; template @@ -2373,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* 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; + } +}; + template class _Fmt_iterator_buffer final : public _Traits, public _Fmt_buffer<_Ty> { private: @@ -2390,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: @@ -3606,9 +3603,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 +3729,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> diff --git a/stl/inc/print b/stl/inc/print index 13f12ea046..6e6b15b9e8 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -24,15 +24,108 @@ _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); + } + + _Stream_lock_guard(const _Stream_lock_guard&) = delete; + _Stream_lock_guard& operator=(const _Stream_lock_guard&) = delete; + + FILE* const _Stream; +}; + +class _Print_to_stream_it { +public: + using iterator_category = output_iterator_tag; + 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=(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; + } + +private: + FILE* _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* const _First, const char* const _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 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=(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; + } + +private: + __std_unicode_console_handle _Console_handle; +}; + +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* 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; + } +}; + +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 +135,19 @@ 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}; + _STD vformat_to(_Print_to_stream_it{_Stream}, _Fmt_str, _Fmt_args); + if (_Add_nl == _Add_newline::_Yes) { + _Print_noformat_nonunicode_nonlocking(_Stream, "\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 +174,86 @@ 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); +} - _STD _Print_noformat_unicode(_Stream, _Output_str); +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}; + + const bool _Was_flush_successful = _CSTD _fflush_nolock(_Stream) == 0; + if (!_Was_flush_successful) [[unlikely]] { + _Throw_system_error(static_cast(errno)); + } + + _STD vformat_to(_Print_to_unicode_console_it{_Console_handle}, _Fmt_str, _Fmt_args); + if (_Add_nl == _Add_newline::_Yes) { + _Print_noformat_unicode_to_console_nonlocking(_Console_handle, "\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 = (enable_nonlocking_formatter_optimization> && ...); 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 +291,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_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(const string_view _Fmt_str, const format_args _Fmt_args) { - _STD vprint_nonunicode(stdout, _Fmt_str, _Fmt_args); +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 diff --git a/stl/inc/stacktrace b/stl/inc/stacktrace index b5b57c00c9..c2e109235b 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 +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/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; } diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index bd1a562828..91a90259b1 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -394,7 +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() +// 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 @@ -1767,7 +1770,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 diff --git a/stl/src/print.cpp b/stl/src/print.cpp index fcd7d263e3..61efeec768 100644 --- a/stl/src/print.cpp +++ b/stl/src/print.cpp @@ -57,27 +57,64 @@ extern "C" { namespace { class _Allocated_string { public: - _Allocated_string() = default; + _Allocated_string() noexcept { + _Buffer[0] = L'\0'; // Activate _Buffer + } - 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.~_Heap_string(); + } + } - [[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 { - _Str.release(); - _Str_capacity = 0; + [[nodiscard]] bool _Grow(const size_t _Capacity) noexcept { + if (_Capacity <= _Str_capacity) { + return true; + } + + 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)); // Activate _Str + + if (!_Str) [[unlikely]] { + _Str_capacity = _Buffer_size; + _Buffer[0] = L'\0'; // Activate _Buffer + return false; + } + + _Str_capacity = _Capacity; + return true; } private: - __crt_unique_heap_ptr _Str; - size_t _Str_capacity = 0; + 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; + + [[nodiscard]] bool _Using_heap() const noexcept { + return _Str_capacity > _Buffer_size; + } + + size_t _Str_capacity = _Buffer_size; + union { + wchar_t _Buffer[_Buffer_size]; + _Heap_string _Str; + }; }; template @@ -187,15 +224,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(), diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 1b16cd1501..356c311b03 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -85,6 +85,10 @@ 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 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 + # 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 @@ -236,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() @@ -581,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. 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..53c28d0c0e --- /dev/null +++ b/tests/std/tests/P3107R5_enabled_specializations/test.compile.pass.cpp @@ -0,0 +1,155 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +struct unoptimized {}; + +template +struct std::formatter : formatter { + template + auto format(const unoptimized&, FormatContext& ctx) const { + return formatter::format("unoptimized", ctx); + } +}; + +static_assert(formattable); +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>); + +// 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>); +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>); 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