https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111052

--- Comment #2 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Jonathan Wakely from comment #1)
> We should also make ranges::copy implement the std::copy optimization for
> copying to ostreambuf_iterator, which is an important performance
> enhancement. That will benefit:
> 
> std::format_to(std::ostreambuf_iterator<char>(std::cout), "");

The following does that, but only gives about 10% improvement:

--- a/libstdc++-v3/include/bits/ranges_algobase.h
+++ b/libstdc++-v3/include/bits/ranges_algobase.h
@@ -201,6 +201,22 @@ namespace ranges
                              copy_backward_result<_Iter, _Out>>
     __copy_or_move_backward(_Iter __first, _Sent __last, _Out __result);

+namespace __detail
+{
+  // True if _Tp is std::istreambuf_iterator<C>.
+  template<typename _Tp> constexpr bool __is_istreambuf_iterator = false;
+  template<typename _CharT>
+    constexpr bool
+    __is_istreambuf_iterator<istreambuf_iterator<_CharT, char_traits<_CharT>>>
+      = true;
+  // True if _Tp is std::ostreambuf_iterator<C>.
+  template<typename _Tp> constexpr bool __is_ostreambuf_iterator = false;
+  template<typename _CharT>
+    constexpr bool
+    __is_ostreambuf_iterator<ostreambuf_iterator<_CharT, char_traits<_CharT>>>
+      = true;
+} // namespace __detail
+
   template<bool _IsMove,
           input_iterator _Iter, sentinel_for<_Iter> _Sent,
           weakly_incrementable _Out>
@@ -217,6 +233,8 @@ namespace ranges
       using __detail::__is_move_iterator;
       using __detail::__is_reverse_iterator;
       using __detail::__is_normal_iterator;
+      using __detail::__is_istreambuf_iterator;
+      using __detail::__is_ostreambuf_iterator;
       if constexpr (__is_move_iterator<_Iter> && same_as<_Iter, _Sent>)
        {
          auto [__in, __out]
@@ -248,6 +266,34 @@ namespace ranges
            = ranges::__copy_or_move<_IsMove>(std::move(__first), __last,
__result.base());
          return {std::move(__in), decltype(__result){__out}};
        }
+      else if constexpr (is_pointer_v<_Iter> && is_pointer_v<_Sent>
+                          && __is_ostreambuf_iterator<_Out>
+                          && requires {
+                            requires is_same_v<iter_value_t<_Iter>, typename
_Out::char_type>;
+                            requires is_same_v<iter_value_t<_Iter>,
iter_value_t<_Sent>>;
+                          })
+       {
+         // copy([const] C*, [const] C*, ostreambuf_iterator<C>)
+         return {__first, std::__copy_move_a2<_IsMove>(__first, __last,
__result)};
+       }
+      else if constexpr (__is_istreambuf_iterator<_Out>
+                          && is_same_v<_Out, iter_value_t<_Iter>*>
+                          && is_same_v<_Iter, _Sent>)
+       {
+         // copy(istreambuf_iterator<C>, istreambuf_iterator<C>, C*)
+         return {__first, std::__copy_move_a2<_IsMove>(__first, __last,
__result)};
+       }
+      else if constexpr (__is_istreambuf_iterator<_Iter> && is_same_v<_Iter,
_Sent>
+                          && !_IsMove && __is_ostreambuf_iterator<_Out>
+                          && requires {
+                            requires is_same_v<typename _Iter::char_type,
+                                               typename _Out::char_type>;
+                          })
+       {
+         // copy(istreambuf_iterator<C>, istreambuf_iterator<C>,
+         //      ostreambuf_iterator<C>)
+         return {__first, std::copy(__first, __last, __result)};
+       }
       else if constexpr (sized_sentinel_for<_Sent, _Iter>)
        {
          if (!std::__is_constant_evaluated())


> The _Iter_sink<_CharT, _Iter> could recognise an ostreambuf_iterator and use
> std::copy, but we should really just make ranges::copy do those
> optimizations.

Better would be for _Iter_sink<_CharT, ostreambuf_iterator<_CharT>> to allow
writing directly to the streambuf's put area. That should be faster than
writing to the sink's buffer and then copying it to the streambuf.

Reply via email to