diff --git a/stl/inc/format b/stl/inc/format index 09d71a7a8db..216afc83e9a 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -169,6 +169,14 @@ concept _CharT_or_bool = same_as<_Ty, _CharT> || same_as<_Ty, bool>; template concept _Format_supported_charT = _Is_any_of_v<_CharT, char, wchar_t>; +template +concept _Has_formatter = requires(_Ty& _Val, _Context& _Ctx) { + _STD declval>>().format(_Val, _Ctx); +}; + +template +concept _Has_const_formatter = _Has_formatter, _Context>; + template struct formatter; @@ -265,13 +273,19 @@ public: public: template - explicit handle(const _Ty& _Val) noexcept + explicit handle(_Ty&& _Val) noexcept : _Ptr(_STD addressof(_Val)), _Format([](basic_format_parse_context<_CharType>& _Parse_ctx, _Context& _Format_ctx, const void* _Ptr) { - typename _Context::template formatter_type<_Ty> _Formatter; + using _Value_type = remove_cvref_t<_Ty>; + typename _Context::template formatter_type<_Value_type> _Formatter; + using _Qualified_type = + conditional_t<_Has_const_formatter<_Value_type, _Context>, const _Value_type, _Value_type>; _Parse_ctx.advance_to(_Formatter.parse(_Parse_ctx)); - _Format_ctx.advance_to(_Formatter.format(*static_cast(_Ptr), _Format_ctx)); - }) {} + _Format_ctx.advance_to(_Formatter.format( + *const_cast<_Qualified_type*>(static_cast(_Ptr)), _Format_ctx)); + }) { + static_assert(_Has_const_formatter<_Ty, _Context> || !is_const_v>); + } void format(basic_format_parse_context<_CharType>& _Parse_ctx, _Context& _Format_ctx) const { _Format(_Parse_ctx, _Format_ctx, _Ptr); @@ -1402,11 +1416,6 @@ public: } }; -template -concept _Has_formatter = requires(const _Ty& _Val, _Context& _Ctx) { - _STD declval>().format(_Val, _Ctx); -}; - template struct _Format_arg_traits { using _Char_type = typename _Context::char_type; @@ -1415,21 +1424,23 @@ struct _Format_arg_traits { // set of basic_format_arg (N4885 [format.arg]). They determine the mapping // from "raw" to "erased" argument type for _Format_arg_store. template <_Has_formatter<_Context> _Ty> - static auto _Phony_basic_format_arg_constructor(const _Ty&) { + static auto _Phony_basic_format_arg_constructor(_Ty&&) { + // per the proposed resolution of LWG-3631 + using _Td = remove_cvref_t<_Ty>; // See N4885 [format.arg]/5 - if constexpr (is_same_v<_Ty, bool>) { + if constexpr (is_same_v<_Td, bool>) { return bool{}; - } else if constexpr (is_same_v<_Ty, _Char_type>) { + } else if constexpr (is_same_v<_Td, _Char_type>) { return _Char_type{}; - } else if constexpr (is_same_v<_Ty, char> && is_same_v<_Char_type, wchar_t>) { + } else if constexpr (is_same_v<_Td, char> && is_same_v<_Char_type, wchar_t>) { return _Char_type{}; - } else if constexpr (signed_integral<_Ty> && sizeof(_Ty) <= sizeof(int)) { + } else if constexpr (signed_integral<_Td> && sizeof(_Td) <= sizeof(int)) { return int{}; - } else if constexpr (unsigned_integral<_Ty> && sizeof(_Ty) <= sizeof(unsigned int)) { + } else if constexpr (unsigned_integral<_Td> && sizeof(_Td) <= sizeof(unsigned int)) { return static_cast(42); - } else if constexpr (signed_integral<_Ty> && sizeof(_Ty) <= sizeof(long long)) { + } else if constexpr (signed_integral<_Td> && sizeof(_Td) <= sizeof(long long)) { return static_cast(42); - } else if constexpr (unsigned_integral<_Ty> && sizeof(_Ty) <= sizeof(unsigned long long)) { + } else if constexpr (unsigned_integral<_Td> && sizeof(_Td) <= sizeof(unsigned long long)) { return static_cast(42); } else { return typename basic_format_arg<_Context>::handle{42}; @@ -1459,10 +1470,10 @@ struct _Format_arg_traits { // clang-format on template - using _Storage_type = decltype(_Phony_basic_format_arg_constructor(_STD declval())); + using _Storage_type = decltype(_Phony_basic_format_arg_constructor(_STD declval<_Ty>())); template - static constexpr size_t _Storage_size = sizeof(_Storage_type<_Ty>); + static constexpr size_t _Storage_size = sizeof(_Storage_type>); }; struct _Format_arg_index { @@ -1523,8 +1534,8 @@ private: } template - void _Store(const size_t _Arg_index, const _Ty& _Val) noexcept { - using _Erased_type = typename _Traits::template _Storage_type<_Ty>; + void _Store(const size_t _Arg_index, _Ty&& _Val) noexcept { + using _Erased_type = typename _Traits::template _Storage_type>; _Basic_format_arg_type _Arg_type; if constexpr (is_same_v<_Erased_type, bool>) { @@ -1560,7 +1571,7 @@ private: } public: - _Format_arg_store(const _Args&... _Vals) noexcept { + _Format_arg_store(_Args&... _Vals) noexcept { _Index_array[0] = {}; size_t _Arg_index = 0; (_Store(_Arg_index++, _Vals), ...); @@ -2948,12 +2959,12 @@ using format_args = basic_format_args; using wformat_args = basic_format_args; template -_NODISCARD auto make_format_args(const _Args&... _Vals) { +_NODISCARD auto make_format_args(_Args&&... _Vals) { return _Format_arg_store<_Context, _Args...>{_Vals...}; } template -_NODISCARD auto make_wformat_args(const _Args&... _Vals) { +_NODISCARD auto make_wformat_args(_Args&&... _Vals) { return _Format_arg_store{_Vals...}; } @@ -3014,22 +3025,22 @@ _OutputIt vformat_to(_OutputIt _Out, const locale& _Loc, const wstring_view _Fmt } template _OutputIt, class... _Types> -_OutputIt format_to(_OutputIt _Out, const _Fmt_string<_Types...> _Fmt, const _Types&... _Args) { +_OutputIt format_to(_OutputIt _Out, const _Fmt_string<_Types...> _Fmt, _Types&&... _Args) { return _STD vformat_to(_STD move(_Out), _Fmt._Str, _STD make_format_args(_Args...)); } template _OutputIt, class... _Types> -_OutputIt format_to(_OutputIt _Out, const _Fmt_wstring<_Types...> _Fmt, const _Types&... _Args) { +_OutputIt format_to(_OutputIt _Out, const _Fmt_wstring<_Types...> _Fmt, _Types&&... _Args) { return _STD vformat_to(_STD move(_Out), _Fmt._Str, _STD make_wformat_args(_Args...)); } template _OutputIt, class... _Types> -_OutputIt format_to(_OutputIt _Out, const locale& _Loc, const _Fmt_string<_Types...> _Fmt, const _Types&... _Args) { +_OutputIt format_to(_OutputIt _Out, const locale& _Loc, const _Fmt_string<_Types...> _Fmt, _Types&&... _Args) { return _STD vformat_to(_STD move(_Out), _Loc, _Fmt._Str, _STD make_format_args(_Args...)); } template _OutputIt, class... _Types> -_OutputIt format_to(_OutputIt _Out, const locale& _Loc, const _Fmt_wstring<_Types...> _Fmt, const _Types&... _Args) { +_OutputIt format_to(_OutputIt _Out, const locale& _Loc, const _Fmt_wstring<_Types...> _Fmt, _Types&&... _Args) { return _STD vformat_to(_STD move(_Out), _Loc, _Fmt._Str, _STD make_wformat_args(_Args...)); } @@ -3076,22 +3087,22 @@ wstring vformat(const locale& _Loc, const wstring_view _Fmt, const wformat_args #undef _TEMPLATE_INT_0_NODISCARD // TRANSITION, VSO-1433873 template -_NODISCARD string format(const _Fmt_string<_Types...> _Fmt, const _Types&... _Args) { +_NODISCARD string format(const _Fmt_string<_Types...> _Fmt, _Types&&... _Args) { return _STD vformat(_Fmt._Str, _STD make_format_args(_Args...)); } template -_NODISCARD wstring format(const _Fmt_wstring<_Types...> _Fmt, const _Types&... _Args) { +_NODISCARD wstring format(const _Fmt_wstring<_Types...> _Fmt, _Types&&... _Args) { return _STD vformat(_Fmt._Str, _STD make_wformat_args(_Args...)); } template -_NODISCARD string format(const locale& _Loc, const _Fmt_string<_Types...> _Fmt, const _Types&... _Args) { +_NODISCARD string format(const locale& _Loc, const _Fmt_string<_Types...> _Fmt, _Types&&... _Args) { return _STD vformat(_Loc, _Fmt._Str, _STD make_format_args(_Args...)); } template -_NODISCARD wstring format(const locale& _Loc, const _Fmt_wstring<_Types...> _Fmt, const _Types&... _Args) { +_NODISCARD wstring format(const locale& _Loc, const _Fmt_wstring<_Types...> _Fmt, _Types&&... _Args) { return _STD vformat(_Loc, _Fmt._Str, _STD make_wformat_args(_Args...)); } @@ -3102,16 +3113,16 @@ struct format_to_n_result { }; template _OutputIt, class... _Types> -format_to_n_result<_OutputIt> format_to_n(_OutputIt _Out, const iter_difference_t<_OutputIt> _Max, - const _Fmt_string<_Types...> _Fmt, const _Types&... _Args) { +format_to_n_result<_OutputIt> format_to_n( + _OutputIt _Out, const iter_difference_t<_OutputIt> _Max, const _Fmt_string<_Types...> _Fmt, _Types&&... _Args) { _Fmt_iterator_buffer<_OutputIt, char, _Fmt_fixed_buffer_traits> _Buf(_STD move(_Out), _Max); _STD vformat_to(_Fmt_it{_Buf}, _Fmt._Str, _STD make_format_args(_Args...)); return {.out = _Buf._Out(), .size = _Buf._Count()}; } template _OutputIt, class... _Types> -format_to_n_result<_OutputIt> format_to_n(_OutputIt _Out, const iter_difference_t<_OutputIt> _Max, - const _Fmt_wstring<_Types...> _Fmt, const _Types&... _Args) { +format_to_n_result<_OutputIt> format_to_n( + _OutputIt _Out, const iter_difference_t<_OutputIt> _Max, const _Fmt_wstring<_Types...> _Fmt, _Types&&... _Args) { _Fmt_iterator_buffer<_OutputIt, wchar_t, _Fmt_fixed_buffer_traits> _Buf(_STD move(_Out), _Max); _STD vformat_to(_Fmt_wit{_Buf}, _Fmt._Str, _STD make_wformat_args(_Args...)); return {.out = _Buf._Out(), .size = _Buf._Count()}; @@ -3119,7 +3130,7 @@ format_to_n_result<_OutputIt> format_to_n(_OutputIt _Out, const iter_difference_ template _OutputIt, class... _Types> format_to_n_result<_OutputIt> format_to_n(_OutputIt _Out, const iter_difference_t<_OutputIt> _Max, const locale& _Loc, - const _Fmt_string<_Types...> _Fmt, const _Types&... _Args) { + const _Fmt_string<_Types...> _Fmt, _Types&&... _Args) { _Fmt_iterator_buffer<_OutputIt, char, _Fmt_fixed_buffer_traits> _Buf(_STD move(_Out), _Max); _STD vformat_to(_Fmt_it{_Buf}, _Loc, _Fmt._Str, _STD make_format_args(_Args...)); return {.out = _Buf._Out(), .size = _Buf._Count()}; @@ -3127,35 +3138,35 @@ format_to_n_result<_OutputIt> format_to_n(_OutputIt _Out, const iter_difference_ template _OutputIt, class... _Types> format_to_n_result<_OutputIt> format_to_n(_OutputIt _Out, const iter_difference_t<_OutputIt> _Max, const locale& _Loc, - const _Fmt_wstring<_Types...> _Fmt, const _Types&... _Args) { + const _Fmt_wstring<_Types...> _Fmt, _Types&&... _Args) { _Fmt_iterator_buffer<_OutputIt, wchar_t, _Fmt_fixed_buffer_traits> _Buf(_STD move(_Out), _Max); _STD vformat_to(_Fmt_wit{_Buf}, _Loc, _Fmt._Str, _STD make_wformat_args(_Args...)); return {.out = _Buf._Out(), .size = _Buf._Count()}; } template -_NODISCARD size_t formatted_size(const _Fmt_string<_Types...> _Fmt, const _Types&... _Args) { +_NODISCARD size_t formatted_size(const _Fmt_string<_Types...> _Fmt, _Types&&... _Args) { _Fmt_counting_buffer _Buf; _STD vformat_to(_Fmt_it{_Buf}, _Fmt._Str, _STD make_format_args(_Args...)); return _Buf._Count(); } template -_NODISCARD size_t formatted_size(const _Fmt_wstring<_Types...> _Fmt, const _Types&... _Args) { +_NODISCARD size_t formatted_size(const _Fmt_wstring<_Types...> _Fmt, _Types&&... _Args) { _Fmt_counting_buffer _Buf; _STD vformat_to(_Fmt_wit{_Buf}, _Fmt._Str, _STD make_wformat_args(_Args...)); return _Buf._Count(); } template -_NODISCARD size_t formatted_size(const locale& _Loc, const _Fmt_string<_Types...> _Fmt, const _Types&... _Args) { +_NODISCARD size_t formatted_size(const locale& _Loc, const _Fmt_string<_Types...> _Fmt, _Types&&... _Args) { _Fmt_counting_buffer _Buf; _STD vformat_to(_Fmt_it{_Buf}, _Loc, _Fmt._Str, _STD make_format_args(_Args...)); return _Buf._Count(); } template -_NODISCARD size_t formatted_size(const locale& _Loc, const _Fmt_wstring<_Types...> _Fmt, const _Types&... _Args) { +_NODISCARD size_t formatted_size(const locale& _Loc, const _Fmt_wstring<_Types...> _Fmt, _Types&&... _Args) { _Fmt_counting_buffer _Buf; _STD vformat_to(_Fmt_wit{_Buf}, _Loc, _Fmt._Str, _STD make_wformat_args(_Args...)); return _Buf._Count(); diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index bcd7886bbba..98896926d32 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -267,6 +267,7 @@ // P2367R0 Remove Misuses Of List-Initialization From Clause 24 Ranges // P2372R3 Fixing Locale Handling In chrono Formatters // P2415R2 What Is A view? +// P2418R2 Add Support For std::generator-like Types To std::format // P2432R1 Fix istream_view // P????R? directory_entry::clear_cache() @@ -1291,7 +1292,7 @@ #define __cpp_lib_erase_if 202002L #if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 and GH-1814 -#define __cpp_lib_format 202106L // P2216R3 std::format Improvements +#define __cpp_lib_format 202110L #endif // _HAS_CXX23 && defined(__cpp_lib_concepts) #define __cpp_lib_generic_unordered_lookup 201811L diff --git a/tests/std/tests/P0645R10_text_formatting_custom_formatting/test.cpp b/tests/std/tests/P0645R10_text_formatting_custom_formatting/test.cpp index 02dd3d93543..76734fdfec8 100644 --- a/tests/std/tests/P0645R10_text_formatting_custom_formatting/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_custom_formatting/test.cpp @@ -4,10 +4,13 @@ #include #include #include +#include #include +#include #include #include #include +#include using namespace std; // copied from the text_formatting_formatting test case @@ -45,6 +48,13 @@ struct basic_custom_formattable_type { string_view string_content; }; +struct not_const_formattable_type { + string_view string_content; + explicit not_const_formattable_type(string_view val) : string_content(val) {} + not_const_formattable_type(const not_const_formattable_type&) = delete; + not_const_formattable_type(not_const_formattable_type&&) = delete; +}; + template <> struct std::formatter { basic_format_parse_context::iterator parse(basic_format_parse_context& parse_ctx) { @@ -59,6 +69,20 @@ struct std::formatter { } }; +template <> +struct std::formatter { + basic_format_parse_context::iterator parse(basic_format_parse_context& parse_ctx) { + if (parse_ctx.begin() != parse_ctx.end()) { + throw format_error{"only empty specs please"}; + } + return parse_ctx.end(); + } + format_context::iterator format(not_const_formattable_type& val, format_context& ctx) { + ctx.advance_to(copy(val.string_content.begin(), val.string_content.end(), ctx.out())); + return ctx.out(); + } +}; + template struct custom_formattable_type { T val; @@ -123,6 +147,28 @@ void test_numeric_mixed_args_custom_formattable_type() { } } +template +void test_format_family_overloads() { + string str; + + assert(format("{}", CustomFormattableType{"f"}) == "f"s); + assert(format(locale{}, "{}", CustomFormattableType{"f"}) == "f"s); + format_to(back_insert_iterator(str), "{}", CustomFormattableType{"f"}); + assert(str == "f"); + str.clear(); + format_to(back_insert_iterator(str), locale{}, "{}", CustomFormattableType{"f"}); + assert(str == "f"); + str.clear(); + format_to_n(back_insert_iterator(str), 5, "{}", CustomFormattableType{"f"}); + assert(str == "f"); + str.clear(); + format_to_n(back_insert_iterator(str), 5, locale{}, "{}", CustomFormattableType{"f"}); + assert(str == "f"); + str.clear(); + assert(formatted_size("{}", CustomFormattableType{"f"}) == 1); + assert(formatted_size(locale{}, "{}", CustomFormattableType{"f"}) == 1); +} + template void test_custom_formattable_type() { test_numeric_custom_formattable_type(); @@ -152,7 +198,8 @@ void test_mixed_custom_formattable_type() { } int main() { - assert(format("{}", basic_custom_formattable_type{"f"}) == "f"s); + test_format_family_overloads(); + test_format_family_overloads(); test_custom_formattable_type(); test_custom_formattable_type(); test_mixed_custom_formattable_type(); diff --git a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp index e48c7671d6c..2fb6af14400 100644 --- a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp @@ -1017,16 +1017,15 @@ void test_spec_replacement_field() { test_string_specs(); } template -void test_size_helper_impl( - const size_t expected_size, const _Basic_format_string fmt, const Args&... args) { - assert(formatted_size(fmt, args...) == expected_size); - assert(formatted_size(locale::classic(), fmt, args...) == expected_size); +void test_size_helper_impl(const size_t expected_size, const _Basic_format_string fmt, Args&&... args) { + assert(formatted_size(fmt, forward(args)...) == expected_size); + assert(formatted_size(locale::classic(), fmt, forward(args)...) == expected_size); const auto signed_size = static_cast(expected_size); basic_string str; { str.resize(expected_size); - const auto res = format_to_n(str.begin(), signed_size, fmt, args...); + const auto res = format_to_n(str.begin(), signed_size, fmt, forward(args)...); assert(res.size == signed_size); assert(res.out - str.begin() == signed_size); assert(res.out == str.end()); @@ -1034,7 +1033,7 @@ void test_size_helper_impl( basic_string locale_str; locale_str.resize(expected_size); - format_to_n(locale_str.begin(), signed_size, locale::classic(), fmt, args...); + format_to_n(locale_str.begin(), signed_size, locale::classic(), fmt, forward(args)...); assert(str == locale_str); assert(locale_str.size() == expected_size); } @@ -1042,7 +1041,7 @@ void test_size_helper_impl( { const auto half_size = expected_size / 2; half_str.resize(half_size); - const auto res = format_to_n(half_str.begin(), static_cast(half_size), fmt, args...); + const auto res = format_to_n(half_str.begin(), static_cast(half_size), fmt, forward(args)...); assert(res.size == signed_size); assert(static_cast(res.out - half_str.begin()) == half_size); assert(res.out == half_str.end()); 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 2379133b46e..481b0cb9572 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 @@ -739,10 +739,10 @@ STATIC_ASSERT(__cpp_lib_filesystem == 201703L); #if _HAS_CXX23 && !defined(__EDG__) // TRANSITION, EDG concepts support and GH-1814 #ifndef __cpp_lib_format #error __cpp_lib_format is not defined -#elif __cpp_lib_format != 202106L -#error __cpp_lib_format is not 202106L +#elif __cpp_lib_format != 202110L +#error __cpp_lib_format is not 202110L #else -STATIC_ASSERT(__cpp_lib_format == 202106L); +STATIC_ASSERT(__cpp_lib_format == 202110L); #endif #else #ifdef __cpp_lib_format