diff --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv --- a/libcxx/docs/Status/Cxx2bIssues.csv +++ b/libcxx/docs/Status/Cxx2bIssues.csv @@ -83,7 +83,7 @@ `3532 `__,"``split_view::inner-iterator::operator++(int)`` should depend on ``Base``","June 2021","","","|ranges|" `3533 `__,"Make ``base() const &`` consistent across iterator wrappers that supports ``input_iterators``","June 2021","|Complete|","14.0","|ranges|" `3536 `__,"Should ``chrono::from_stream()`` assign zero to duration for failure?","June 2021","","","|chrono|" -`3539 `__,"``format_to`` must not copy models of ``output_iterator``","June 2021","","","|format|" +`3539 `__,"``format_to`` must not copy models of ``output_iterator``","June 2021","|Complete|","14.0","|format|" `3540 `__,"ยง[format.arg] There should be no const in ``basic_format_arg(const T* p)``","June 2021","|Complete|","14.0","|format|" `3541 `__,"``indirectly_readable_traits`` should be SFINAE-friendly for all types","June 2021","|Complete|","14.0","|ranges|" `3542 `__,"``basic_format_arg`` mishandles ``basic_string_view`` with custom traits","June 2021","|Complete|","14.0","|format|" @@ -120,7 +120,7 @@ `3561 `__,"Issue with internal counter in ``discard_block_engine``","October 2021","","" `3563 `__,"``keys_view`` example is broken","October 2021","","","|ranges|" `3566 `__,"Constraint recursion for ``operator<=>(optional, U)``","October 2021","","","|spaceship|" -`3567 `__,"Formatting move-only iterators take two","October 2021","","","|format|" +`3567 `__,"Formatting move-only iterators take two","October 2021","|Complete|","16.0","|format|" `3568 `__,"``basic_istream_view`` needs to initialize ``value_``","October 2021","|Complete|","16.0","|ranges|" `3570 `__,"``basic_osyncstream::emit`` should be an unformatted output function","October 2021","","" `3571 `__,"``flush_emit`` should set ``badbit`` if the ``emit`` call fails","October 2021","","" diff --git a/libcxx/include/__format/buffer.h b/libcxx/include/__format/buffer.h --- a/libcxx/include/__format/buffer.h +++ b/libcxx/include/__format/buffer.h @@ -14,6 +14,7 @@ #include <__algorithm/fill_n.h> #include <__algorithm/max.h> #include <__algorithm/min.h> +#include <__algorithm/ranges_copy_n.h> #include <__algorithm/transform.h> #include <__algorithm/unwrap_iter.h> #include <__config> @@ -275,10 +276,10 @@ _LIBCPP_HIDE_FROM_ABI explicit __writer_iterator(_OutIt __out_it) : __out_it_{_VSTD::move(__out_it)} {} - _LIBCPP_HIDE_FROM_ABI _OutIt __out_it() { return __out_it_; } + _LIBCPP_HIDE_FROM_ABI _OutIt __out_it() && { return std::move(__out_it_); } _LIBCPP_HIDE_FROM_ABI void __flush(_CharT* __ptr, size_t __n) { - __out_it_ = _VSTD::copy_n(__ptr, __n, _VSTD::move(__out_it_)); + __out_it_ = std::ranges::copy_n(__ptr, __n, std::move(__out_it_)).out; } private: diff --git a/libcxx/include/__format/format_context.h b/libcxx/include/__format/format_context.h --- a/libcxx/include/__format/format_context.h +++ b/libcxx/include/__format/format_context.h @@ -100,8 +100,8 @@ return *__loc_; } #endif - _LIBCPP_HIDE_FROM_ABI iterator out() { return __out_it_; } - _LIBCPP_HIDE_FROM_ABI void advance_to(iterator __it) { __out_it_ = __it; } + _LIBCPP_HIDE_FROM_ABI iterator out() { return std::move(__out_it_); } + _LIBCPP_HIDE_FROM_ABI void advance_to(iterator __it) { __out_it_ = std::move(__it); } private: iterator __out_it_; diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/advance_to.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/advance_to.pass.cpp --- a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/advance_to.pass.cpp +++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/advance_to.pass.cpp @@ -18,18 +18,20 @@ #include "test_macros.h" #include "test_format_context.h" +#include "test_iterators.h" template void test( std::basic_format_args> args) { { - std::basic_string str[3]; - std::basic_format_context context = test_format_context_create(OutIt{str[0]}, args); - context.out() = CharT('a'); - context.advance_to(OutIt{str[1]}); - context.out() = CharT('b'); - context.advance_to(OutIt{str[2]}); - context.out() = CharT('c'); + std::basic_string str[3] = { + std::basic_string{1}, std::basic_string{1}, std::basic_string{1}}; + std::basic_format_context context = test_format_context_create(OutIt{str[0].begin()}, args); + *context.out() = CharT('a'); + context.advance_to(OutIt{str[1].begin()}); + *context.out() = CharT('b'); + context.advance_to(OutIt{str[2].begin()}); + *context.out() = CharT('c'); assert(str[0].size() == 1); assert(str[0].front() == CharT('a')); @@ -42,27 +44,25 @@ void test() { test(std::basic_format_args( - std::make_format_args>, char>>())); + std::make_format_args< + std::basic_format_context::iterator>, char>>())); #ifndef TEST_HAS_NO_WIDE_CHARACTERS test(std::basic_format_args( - std::make_format_args>, wchar_t>>())); + std::make_format_args< + std::basic_format_context::iterator>, wchar_t>>())); #endif #ifndef TEST_HAS_NO_CHAR8_T test(std::basic_format_args( - std::make_format_args>, char8_t>>())); + std::make_format_args< + std::basic_format_context::iterator>, char8_t>>())); #endif test(std::basic_format_args( - std::make_format_args>, - char16_t>>())); + std::make_format_args< + std::basic_format_context::iterator>, char16_t>>())); test(std::basic_format_args( - std::make_format_args>, - char32_t>>())); + std::make_format_args< + std::basic_format_context::iterator>, char32_t>>())); } int main(int, char**) { diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/out.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/out.pass.cpp --- a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/out.pass.cpp +++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/out.pass.cpp @@ -18,17 +18,20 @@ #include "test_macros.h" #include "test_format_context.h" +#include "test_iterators.h" template void test( std::basic_format_args> args) { { - std::basic_string str; - OutIt out_it{str}; - std::basic_format_context context = test_format_context_create(out_it, args); - context.out() = CharT('a'); - context.out() = CharT('b'); - context.out() = CharT('c'); + std::basic_string str = std::basic_string(3, CharT(' ')); + std::basic_format_context context = test_format_context_create(OutIt{str.begin()}, args); + + // Note this operation is moving the iterator + OutIt out_it = context.out(); + *out_it++ = CharT('a'); + *out_it++ = CharT('b'); + *out_it++ = CharT('c'); assert(str.size() == 3); assert(str[0] == CharT('a')); @@ -39,26 +42,25 @@ void test() { test(std::basic_format_args( - std::make_format_args>, char>>())); + std::make_format_args< + std::basic_format_context::iterator>, char>>())); + #ifndef TEST_HAS_NO_WIDE_CHARACTERS test(std::basic_format_args( - std::make_format_args>, wchar_t>>())); + std::make_format_args< + std::basic_format_context::iterator>, wchar_t>>())); #endif #ifndef TEST_HAS_NO_CHAR8_T test(std::basic_format_args( - std::make_format_args>, char8_t>>())); + std::make_format_args< + std::basic_format_context::iterator>, char8_t>>())); #endif test(std::basic_format_args( - std::make_format_args>, - char16_t>>())); + std::make_format_args< + std::basic_format_context::iterator>, char16_t>>())); test(std::basic_format_args( - std::make_format_args>, - char32_t>>())); + std::make_format_args< + std::basic_format_context::iterator>, char32_t>>())); } int main(int, char**) { diff --git a/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp --- a/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp @@ -32,6 +32,7 @@ #include "format_tests.h" #include "string_literal.h" #include "test_format_string.h" +#include "test_iterators.h" auto test = []( @@ -55,10 +56,11 @@ { assert(expected.size() < 4096 && "Update the size of the buffer."); CharT out[4096]; - CharT* it = std::format_to(out, std::locale(), fmt, std::forward(args)...); - assert(std::distance(out, it) == int(expected.size())); + cpp20_output_iterator it = + std::format_to(cpp20_output_iterator{out}, std::locale(), fmt, std::forward(args)...); + assert(std::distance(out, base(it)) == int(expected.size())); // Convert to std::string since output contains '\0' for boolean tests. - assert(std::basic_string(out, it) == expected); + assert(std::basic_string(out, base(it)) == expected); } }; diff --git a/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp --- a/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp @@ -29,6 +29,7 @@ #include "format_tests.h" #include "string_literal.h" #include "test_format_string.h" +#include "test_iterators.h" auto test = []( @@ -52,10 +53,10 @@ { assert(expected.size() < 4096 && "Update the size of the buffer."); CharT out[4096]; - CharT* it = std::format_to(out, fmt, std::forward(args)...); - assert(std::distance(out, it) == int(expected.size())); + cpp20_output_iterator it = std::format_to(cpp20_output_iterator{out}, fmt, std::forward(args)...); + assert(std::distance(out, base(it)) == int(expected.size())); // Convert to std::string since output contains '\0' for boolean tests. - assert(std::basic_string(out, it) == expected); + assert(std::basic_string(out, base(it)) == expected); } };