diff --git a/stl/inc/format b/stl/inc/format index 5b976f2f5d8..60588d2a63c 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -125,10 +125,18 @@ static_assert(static_cast(_Basic_format_arg_type::_Custom_type) < 16, "must _NODISCARD constexpr bool _Is_integral_fmt_type(_Basic_format_arg_type _Ty) { return _Ty > _Basic_format_arg_type::_None && _Ty <= _Basic_format_arg_type::_Char_type; } + _NODISCARD constexpr bool _Is_arithmetic_fmt_type(_Basic_format_arg_type _Ty) { return _Ty > _Basic_format_arg_type::_None && _Ty <= _Basic_format_arg_type::_Long_double_type; } +#if _HAS_CXX23 +_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; +} +#endif // _HAS_CXX23 + struct _Auto_id_tag { explicit _Auto_id_tag() = default; }; @@ -3580,8 +3588,18 @@ struct formatter { _FMT_P2286_BEGIN template struct _Formatter_base { +private: using _Pc = basic_format_parse_context<_CharT>; +public: +#if _HAS_CXX23 + constexpr void _Set_debug_format() noexcept + requires (_Is_debug_enabled_fmt_type(_ArgType)) + { + _Specs._Type = '?'; + } +#endif // _HAS_CXX23 + constexpr _Pc::iterator parse(_Pc& _ParseCtx) { _Specs_checker<_Dynamic_specs_handler<_Pc>> _Handler(_Dynamic_specs_handler<_Pc>{_Specs, _ParseCtx}, _ArgType); const auto _It = _Parse_format_specs(_ParseCtx._Unchecked_begin(), _ParseCtx._Unchecked_end(), _Handler); @@ -3634,35 +3652,80 @@ _FORMAT_SPECIALIZE_FOR(short, _Basic_format_arg_type::_Int_type); _FORMAT_SPECIALIZE_FOR(unsigned short, _Basic_format_arg_type::_UInt_type); _FORMAT_SPECIALIZE_FOR(long, _Basic_format_arg_type::_Int_type); _FORMAT_SPECIALIZE_FOR(unsigned long, _Basic_format_arg_type::_UInt_type); -_FORMAT_SPECIALIZE_FOR(char, _Basic_format_arg_type::_Char_type); _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 +// not using the macro because we'd like to add 'set_debug_format' member function in C++23 mode +template <_Format_supported_charT _CharT> +struct formatter : _Formatter_base { +#if _HAS_CXX23 + constexpr void set_debug_format() noexcept { + this->_Set_debug_format(); + } +#endif // _HAS_CXX23 +}; + // not using the macro because we'd like to avoid the formatter specialization template <> -struct formatter : _Formatter_base {}; +struct formatter : _Formatter_base { +#if _HAS_CXX23 + constexpr void set_debug_format() noexcept { + _Set_debug_format(); + } +#endif // _HAS_CXX23 +}; // We could use the macro for these specializations, but it's confusing to refer to symbols that are defined // inside the macro in the macro's "call". template <_Format_supported_charT _CharT> -struct formatter<_CharT*, _CharT> : _Formatter_base<_CharT*, _CharT, _Basic_format_arg_type::_CString_type> {}; +struct formatter<_CharT*, _CharT> : _Formatter_base<_CharT*, _CharT, _Basic_format_arg_type::_CString_type> { +#if _HAS_CXX23 + constexpr void set_debug_format() noexcept { + this->_Set_debug_format(); + } +#endif // _HAS_CXX23 +}; template <_Format_supported_charT _CharT> struct formatter - : _Formatter_base {}; + : _Formatter_base { +#if _HAS_CXX23 + constexpr void set_debug_format() noexcept { + this->_Set_debug_format(); + } +#endif // _HAS_CXX23 +}; template <_Format_supported_charT _CharT, size_t _Nx> -struct formatter<_CharT[_Nx], _CharT> : _Formatter_base<_CharT[_Nx], _CharT, _Basic_format_arg_type::_CString_type> {}; +struct formatter<_CharT[_Nx], _CharT> : _Formatter_base<_CharT[_Nx], _CharT, _Basic_format_arg_type::_CString_type> { +#if _HAS_CXX23 + constexpr void set_debug_format() noexcept { + this->_Set_debug_format(); + } +#endif // _HAS_CXX23 +}; template <_Format_supported_charT _CharT, class _Traits, class _Allocator> struct formatter, _CharT> - : _Formatter_base, _CharT, _Basic_format_arg_type::_String_type> {}; + : _Formatter_base, _CharT, _Basic_format_arg_type::_String_type> { +#if _HAS_CXX23 + constexpr void set_debug_format() noexcept { + this->_Set_debug_format(); + } +#endif // _HAS_CXX23 +}; template <_Format_supported_charT _CharT, class _Traits> struct formatter, _CharT> - : _Formatter_base, _CharT, _Basic_format_arg_type::_String_type> {}; + : _Formatter_base, _CharT, _Basic_format_arg_type::_String_type> { +#if _HAS_CXX23 + constexpr void set_debug_format() noexcept { + this->_Set_debug_format(); + } +#endif // _HAS_CXX23 +}; _EXPORT_STD template struct basic_format_string { diff --git a/tests/std/include/test_format_support.hpp b/tests/std/include/test_format_support.hpp index 969f6f57bb0..c8b2a218f45 100644 --- a/tests/std/include/test_format_support.hpp +++ b/tests/std/include/test_format_support.hpp @@ -26,6 +26,10 @@ struct choose_literal { static constexpr char choose(char c, wchar_t) { return c; } + + static constexpr std::string_view choose(std::string_view sv, std::wstring_view) { + return sv; + } }; template <> @@ -37,6 +41,10 @@ struct choose_literal { static constexpr wchar_t choose(char, wchar_t c) { return c; } + + static constexpr std::wstring_view choose(std::string_view, std::wstring_view sv) { + return sv; + } }; #define TYPED_LITERAL(CharT, Literal) (choose_literal::choose(Literal, L##Literal)) diff --git a/tests/std/test.lst b/tests/std/test.lst index c2190304dcc..d2d3d1ac079 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -581,9 +581,10 @@ tests\P2278R4_const_span tests\P2278R4_ranges_const_iterator_machinery tests\P2278R4_ranges_const_range_machinery tests\P2278R4_views_as_const -tests\P2286R8_formatting_ranges -tests\P2286R8_formatting_ranges_legacy_text_encoding -tests\P2286R8_formatting_ranges_utf8 +tests\P2286R8_text_formatting_debug_enabled_specializations +tests\P2286R8_text_formatting_escaping +tests\P2286R8_text_formatting_escaping_legacy_text_encoding +tests\P2286R8_text_formatting_escaping_utf8 tests\P2302R4_ranges_alg_contains tests\P2302R4_ranges_alg_contains_subrange tests\P2321R2_proxy_reference diff --git a/tests/std/tests/P2286R8_formatting_ranges/env.lst b/tests/std/tests/P2286R8_text_formatting_debug_enabled_specializations/env.lst similarity index 100% rename from tests/std/tests/P2286R8_formatting_ranges/env.lst rename to tests/std/tests/P2286R8_text_formatting_debug_enabled_specializations/env.lst diff --git a/tests/std/tests/P2286R8_text_formatting_debug_enabled_specializations/test.cpp b/tests/std/tests/P2286R8_text_formatting_debug_enabled_specializations/test.cpp new file mode 100644 index 00000000000..d5ac1fad875 --- /dev/null +++ b/tests/std/tests/P2286R8_text_formatting_debug_enabled_specializations/test.cpp @@ -0,0 +1,211 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define STR(Str) TYPED_LITERAL(CharT, Str) + +using namespace std; + +template +concept DebugEnabledSpecialization = is_default_constructible_v && requires(F& fmt) { + { fmt.set_debug_format() } noexcept -> same_as; +}; + +template +consteval bool check_debug_enabled_specializations() { + static_assert(DebugEnabledSpecialization>); + static_assert(DebugEnabledSpecialization>); + static_assert(DebugEnabledSpecialization>); + static_assert(DebugEnabledSpecialization>); + static_assert(DebugEnabledSpecialization>); + static_assert(DebugEnabledSpecialization, CharT>>); + static_assert(DebugEnabledSpecialization, CharT>>); + static_assert(DebugEnabledSpecialization, CharT>>); + + static_assert(!DebugEnabledSpecialization>); + static_assert(!DebugEnabledSpecialization>); + static_assert(!DebugEnabledSpecialization>); + static_assert(!DebugEnabledSpecialization>); + static_assert(!DebugEnabledSpecialization>); + + static_assert(!DebugEnabledSpecialization>); + // NB: formatter is special case, see below + static_assert(!DebugEnabledSpecialization>); + static_assert(!DebugEnabledSpecialization>); + static_assert(!DebugEnabledSpecialization>); + + static_assert(!DebugEnabledSpecialization>); + static_assert(!DebugEnabledSpecialization>); + static_assert(!DebugEnabledSpecialization>); + static_assert(!DebugEnabledSpecialization>); + + // NB: wchar_t might be defined as a typedef for unsigned short (with '/Zc:wchar_t-') + static_assert(DebugEnabledSpecialization> == same_as); + + return true; +} + +template +struct Holder { + char narrow_ch; + CharT ch; + const CharT* const_ptr; + basic_string str; + CharT* non_const_ptr; + basic_string_view str_view; + CharT arr[11]; +}; + +// holder-format-specs: +// member debug-format(opt) +// member: +// 0 1 2 ... N (index of member object, single digit) +// debug-format: +// $ (use debug format) +template +struct std::formatter, CharT> { +public: + constexpr auto parse(basic_format_parse_context& ctx) { + auto it = ctx.begin(); + if (it == ctx.end() || *it == STR('}')) { + throw format_error{"Invalid holder-format-specs."}; + } + + if (STR('0') <= *it && *it <= STR('9')) { + member_index = *it - STR('0'); + } else { + throw format_error{"Expected member index in holder-format-specs."}; + } + + ++it; + if (it == ctx.end() || *it == STR('}')) { + return it; + } + + if (*it == '$') { + switch (member_index) { + case 0: + fmt0.set_debug_format(); + break; + case 1: + fmt1.set_debug_format(); + break; + case 2: + fmt2.set_debug_format(); + break; + case 3: + fmt3.set_debug_format(); + break; + case 4: + fmt4.set_debug_format(); + break; + case 5: + fmt5.set_debug_format(); + break; + case 6: + fmt6.set_debug_format(); + break; + } + } else { + throw format_error{"Unexpected symbols in holder-format-specs."}; + } + + ++it; + if (it != ctx.end() && *it != STR('}')) { + throw format_error{"Expected '}' at the end of holder-format-specs."}; + } + + return it; + } + + template + auto format(const Holder& val, FormatContext& ctx) const { + switch (member_index) { + case 0: + return fmt0.format(val.narrow_ch, ctx); + case 1: + return fmt1.format(val.ch, ctx); + case 2: + return fmt2.format(val.const_ptr, ctx); + case 3: + return fmt3.format(val.str, ctx); + case 4: + return fmt4.format(val.non_const_ptr, ctx); + case 5: + return fmt5.format(val.str_view, ctx); + case 6: + return fmt6.format(val.arr, ctx); + } + + unreachable(); + } + +private: + int member_index{-1}; + + formatter fmt0; + formatter fmt1; + formatter fmt2; + formatter, CharT> fmt3; + formatter fmt4; + formatter, CharT> fmt5; + formatter fmt6; +}; + +template +void check_set_debug_format_function() { + Holder val; + + val.narrow_ch = '\t'; + val.ch = STR('\t'); + val.const_ptr = STR("const\tCharT\t*"); + val.str = STR("basic\tstring"); + val.non_const_ptr = val.str.data(); + val.str_view = STR("basic\tstring\tview"); + ranges::copy(STR("CharT\t[11]\0"sv), val.arr); + + assert(format(STR("{:0}"), val) == STR("\t")); + assert(format(STR("{:1}"), val) == STR("\t")); + assert(format(STR("{:2}"), val) == STR("const\tCharT\t*")); + assert(format(STR("{:3}"), val) == STR("basic\tstring")); + assert(format(STR("{:4}"), val) == STR("basic\tstring")); + assert(format(STR("{:5}"), val) == STR("basic\tstring\tview")); + assert(format(STR("{:6}"), val) == STR("CharT\t[11]")); + + assert(format(STR("{:0$}"), val) == STR(R"('\t')")); + assert(format(STR("{:1$}"), val) == STR(R"('\t')")); + assert(format(STR("{:2$}"), val) == STR(R"("const\tCharT\t*")")); + assert(format(STR("{:3$}"), val) == STR(R"("basic\tstring")")); + assert(format(STR("{:4$}"), val) == STR(R"("basic\tstring")")); + assert(format(STR("{:5$}"), val) == STR(R"("basic\tstring\tview")")); + assert(format(STR("{:6$}"), val) == STR(R"("CharT\t[11]")")); +} + +void set_debug_format(auto&) {} + +struct name_lookup_in_formatter_checker : formatter { + auto parse(auto& ctx) { // COMPILE-ONLY + set_debug_format(*this); + return ctx.begin(); + } +}; + +int main() { + static_assert(check_debug_enabled_specializations()); + static_assert(check_debug_enabled_specializations()); + + check_set_debug_format_function(); + check_set_debug_format_function(); +} diff --git a/tests/std/tests/P2286R8_text_formatting_escaping/env.lst b/tests/std/tests/P2286R8_text_formatting_escaping/env.lst new file mode 100644 index 00000000000..18e2d7c71ec --- /dev/null +++ b/tests/std/tests/P2286R8_text_formatting_escaping/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P2286R8_formatting_ranges/test.cpp b/tests/std/tests/P2286R8_text_formatting_escaping/test.cpp similarity index 100% rename from tests/std/tests/P2286R8_formatting_ranges/test.cpp rename to tests/std/tests/P2286R8_text_formatting_escaping/test.cpp diff --git a/tests/std/tests/P2286R8_formatting_ranges_legacy_text_encoding/env.lst b/tests/std/tests/P2286R8_text_formatting_escaping_legacy_text_encoding/env.lst similarity index 100% rename from tests/std/tests/P2286R8_formatting_ranges_legacy_text_encoding/env.lst rename to tests/std/tests/P2286R8_text_formatting_escaping_legacy_text_encoding/env.lst diff --git a/tests/std/tests/P2286R8_formatting_ranges_legacy_text_encoding/test.cpp b/tests/std/tests/P2286R8_text_formatting_escaping_legacy_text_encoding/test.cpp similarity index 100% rename from tests/std/tests/P2286R8_formatting_ranges_legacy_text_encoding/test.cpp rename to tests/std/tests/P2286R8_text_formatting_escaping_legacy_text_encoding/test.cpp diff --git a/tests/std/tests/P2286R8_formatting_ranges_utf8/env.lst b/tests/std/tests/P2286R8_text_formatting_escaping_utf8/env.lst similarity index 100% rename from tests/std/tests/P2286R8_formatting_ranges_utf8/env.lst rename to tests/std/tests/P2286R8_text_formatting_escaping_utf8/env.lst diff --git a/tests/std/tests/P2286R8_formatting_ranges_utf8/test.cpp b/tests/std/tests/P2286R8_text_formatting_escaping_utf8/test.cpp similarity index 100% rename from tests/std/tests/P2286R8_formatting_ranges_utf8/test.cpp rename to tests/std/tests/P2286R8_text_formatting_escaping_utf8/test.cpp