[libc++][format] Move iterators when needed.
authorMark de Wever <koraq@xs4all.nl>
Sat, 23 Oct 2021 17:11:02 +0000 (19:11 +0200)
committerMark de Wever <koraq@xs4all.nl>
Tue, 18 Oct 2022 16:45:41 +0000 (18:45 +0200)
LWG-3539 was already implemented but not marked as done.
LWG-3567 is implemented in this commit.

Reviewed By: ldionne, #libc

Differential Revision: https://reviews.llvm.org/D112368

libcxx/docs/Status/Cxx2bIssues.csv
libcxx/include/__format/buffer.h
libcxx/include/__format/format_context.h
libcxx/test/std/utilities/format/format.formatter/format.context/format.context/advance_to.pass.cpp
libcxx/test/std/utilities/format/format.formatter/format.context/format.context/out.pass.cpp
libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp
libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp

index f039c25..1a29719 100644 (file)
@@ -83,7 +83,7 @@
 `3532 <https://wg21.link/LWG3532>`__,"``split_view<V, P>::inner-iterator<true>::operator++(int)`` should depend on ``Base``","June 2021","","","|ranges|"
 `3533 <https://wg21.link/LWG3533>`__,"Make ``base() const &`` consistent across iterator wrappers that supports ``input_iterators``","June 2021","|Complete|","14.0","|ranges|"
 `3536 <https://wg21.link/LWG3536>`__,"Should ``chrono::from_stream()`` assign zero to duration for failure?","June 2021","","","|chrono|"
-`3539 <https://wg21.link/LWG3539>`__,"``format_to`` must not copy models of ``output_iterator<const charT&>``","June 2021","","","|format|"
+`3539 <https://wg21.link/LWG3539>`__,"``format_to`` must not copy models of ``output_iterator<const charT&>``","June 2021","|Complete|","14.0","|format|"
 `3540 <https://wg21.link/LWG3540>`__,"ยง[format.arg] There should be no const in ``basic_format_arg(const T* p)``","June 2021","|Complete|","14.0","|format|"
 `3541 <https://wg21.link/LWG3541>`__,"``indirectly_readable_traits`` should be SFINAE-friendly for all types","June 2021","|Complete|","14.0","|ranges|"
 `3542 <https://wg21.link/LWG3542>`__,"``basic_format_arg`` mishandles ``basic_string_view`` with custom traits","June 2021","|Complete|","14.0","|format|"
 `3561 <https://wg21.link/LWG3561>`__,"Issue with internal counter in ``discard_block_engine``","October 2021","",""
 `3563 <https://wg21.link/LWG3563>`__,"``keys_view`` example is broken","October 2021","","","|ranges|"
 `3566 <https://wg21.link/LWG3566>`__,"Constraint recursion for ``operator<=>(optional<T>, U)``","October 2021","","","|spaceship|"
-`3567 <https://wg21.link/LWG3567>`__,"Formatting move-only iterators take two","October 2021","","","|format|"
+`3567 <https://wg21.link/LWG3567>`__,"Formatting move-only iterators take two","October 2021","|Complete|","16.0","|format|"
 `3568 <https://wg21.link/LWG3568>`__,"``basic_istream_view`` needs to initialize ``value_``","October 2021","|Complete|","16.0","|ranges|"
 `3570 <https://wg21.link/LWG3570>`__,"``basic_osyncstream::emit`` should be an unformatted output function","October 2021","",""
 `3571 <https://wg21.link/LWG3571>`__,"``flush_emit`` should set ``badbit`` if the ``emit`` call fails","October 2021","",""
index 504f871..4f7577e 100644 (file)
@@ -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 @@ public:
   _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:
index 37d4fe1..19468de 100644 (file)
@@ -100,8 +100,8 @@ public:
     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_;
index 04f3f50..e61188a 100644 (file)
 
 #include "test_macros.h"
 #include "test_format_context.h"
+#include "test_iterators.h"
 
 template <class OutIt, class CharT>
 void test(
     std::basic_format_args<std::basic_format_context<OutIt, CharT>> args) {
   {
-    std::basic_string<CharT> 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<CharT> str[3] = {
+        std::basic_string<CharT>{1}, std::basic_string<CharT>{1}, std::basic_string<CharT>{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(
 
 void test() {
   test(std::basic_format_args(
-      std::make_format_args<std::basic_format_context<
-          std::back_insert_iterator<std::basic_string<char>>, char>>()));
+      std::make_format_args<
+          std::basic_format_context<cpp20_output_iterator<std::basic_string<char>::iterator>, char>>()));
 
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test(std::basic_format_args(
-      std::make_format_args<std::basic_format_context<
-          std::back_insert_iterator<std::basic_string<wchar_t>>, wchar_t>>()));
+      std::make_format_args<
+          std::basic_format_context<cpp20_output_iterator<std::basic_string<wchar_t>::iterator>, wchar_t>>()));
 #endif
 #ifndef TEST_HAS_NO_CHAR8_T
   test(std::basic_format_args(
-      std::make_format_args<std::basic_format_context<
-          std::back_insert_iterator<std::basic_string<char8_t>>, char8_t>>()));
+      std::make_format_args<
+          std::basic_format_context<cpp20_output_iterator<std::basic_string<char8_t>::iterator>, char8_t>>()));
 #endif
   test(std::basic_format_args(
-      std::make_format_args<std::basic_format_context<
-          std::back_insert_iterator<std::basic_string<char16_t>>,
-          char16_t>>()));
+      std::make_format_args<
+          std::basic_format_context<cpp20_output_iterator<std::basic_string<char16_t>::iterator>, char16_t>>()));
   test(std::basic_format_args(
-      std::make_format_args<std::basic_format_context<
-          std::back_insert_iterator<std::basic_string<char32_t>>,
-          char32_t>>()));
+      std::make_format_args<
+          std::basic_format_context<cpp20_output_iterator<std::basic_string<char32_t>::iterator>, char32_t>>()));
 }
 
 int main(int, char**) {
index c358a33..effcec7 100644 (file)
 
 #include "test_macros.h"
 #include "test_format_context.h"
+#include "test_iterators.h"
 
 template <class OutIt, class CharT>
 void test(
     std::basic_format_args<std::basic_format_context<OutIt, CharT>> args) {
   {
-    std::basic_string<CharT> 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<CharT> str      = std::basic_string<CharT>(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(
 
 void test() {
   test(std::basic_format_args(
-      std::make_format_args<std::basic_format_context<
-          std::back_insert_iterator<std::basic_string<char>>, char>>()));
+      std::make_format_args<
+          std::basic_format_context<cpp20_output_iterator<std::basic_string<char>::iterator>, char>>()));
+
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test(std::basic_format_args(
-      std::make_format_args<std::basic_format_context<
-          std::back_insert_iterator<std::basic_string<wchar_t>>, wchar_t>>()));
+      std::make_format_args<
+          std::basic_format_context<cpp20_output_iterator<std::basic_string<wchar_t>::iterator>, wchar_t>>()));
 #endif
 #ifndef TEST_HAS_NO_CHAR8_T
   test(std::basic_format_args(
-      std::make_format_args<std::basic_format_context<
-          std::back_insert_iterator<std::basic_string<char8_t>>, char8_t>>()));
+      std::make_format_args<
+          std::basic_format_context<cpp20_output_iterator<std::basic_string<char8_t>::iterator>, char8_t>>()));
 #endif
   test(std::basic_format_args(
-      std::make_format_args<std::basic_format_context<
-          std::back_insert_iterator<std::basic_string<char16_t>>,
-          char16_t>>()));
+      std::make_format_args<
+          std::basic_format_context<cpp20_output_iterator<std::basic_string<char16_t>::iterator>, char16_t>>()));
   test(std::basic_format_args(
-      std::make_format_args<std::basic_format_context<
-          std::back_insert_iterator<std::basic_string<char32_t>>,
-          char32_t>>()));
+      std::make_format_args<
+          std::basic_format_context<cpp20_output_iterator<std::basic_string<char32_t>::iterator>, char32_t>>()));
 }
 
 int main(int, char**) {
index f94bfef..b4726c2 100644 (file)
@@ -32,6 +32,7 @@
 #include "format_tests.h"
 #include "string_literal.h"
 #include "test_format_string.h"
+#include "test_iterators.h"
 
 auto test =
     []<class CharT, class... Args>(
@@ -55,10 +56,11 @@ auto test =
       {
         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>(args)...);
-        assert(std::distance(out, it) == int(expected.size()));
+        cpp20_output_iterator<CharT*> it =
+            std::format_to(cpp20_output_iterator{out}, std::locale(), fmt, std::forward<Args>(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<CharT>(out, it) == expected);
+        assert(std::basic_string<CharT>(out, base(it)) == expected);
       }
     };
 
index 0fbd665..04e4f1f 100644 (file)
@@ -29,6 +29,7 @@
 #include "format_tests.h"
 #include "string_literal.h"
 #include "test_format_string.h"
+#include "test_iterators.h"
 
 auto test =
     []<class CharT, class... Args>(
@@ -52,10 +53,10 @@ auto test =
       {
         assert(expected.size() < 4096 && "Update the size of the buffer.");
         CharT out[4096];
-        CharT* it = std::format_to(out, fmt, std::forward<Args>(args)...);
-        assert(std::distance(out, it) == int(expected.size()));
+        cpp20_output_iterator<CharT*> it = std::format_to(cpp20_output_iterator{out}, fmt, std::forward<Args>(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<CharT>(out, it) == expected);
+        assert(std::basic_string<CharT>(out, base(it)) == expected);
       }
     };