On Thu, 17 Oct 2024, Jonathan Wakely wrote:

> I've split this out of "Refactor std::uninitialized_{copy, fill, fill_n}"
> because this part can be done separately. Call it [PATCH -1/7] if you
> like :-)
> 
> This fixes the ordering problem that Patrick noticed in [PATCH 1/7], and
> adds a test for it. It also updates the comments as was previously done
> in [PATCH 2/7], which Patrick noted could have been done when moving the
> functions into stl_iterator.h.

LGTM

> 
> Note that the __niter_base overloads for reverse_iterator and
> move_iterator call __niter_base unqualified, which means that in
> contrast to all other uses of __niter_base they *do* use ADL to find the
> next __niter_base to call. I think that's necessary so that it works for
> both reverse_iterator<move_iterator<I>> and the inverse order,
> move_iterator<reverse_iterator<I>>. I haven't changed that here, they
> still use unqualified calls.

IIUC since the overloads' constraints are mutually recursive it'd be
kind of awkward to avoid ADL.  Dunno how badly we want to avoid ADL
here, but I think one way would be to define the overloads as static
member functions and make the calls within the signature
dependently-scoped, e.g.

  struct __niter_base_overloads {
    template<typename _Iterator, typename _Self = __niter_base_overloads>
      _GLIBCXX20_CONSTEXPR
      static auto
      __niter_base(reverse_iterator<_Iterator> __it)
      -> decltype(__make_reverse_iterator(_Self::__niter_base(__it.base())))
      { return __make_reverse_iterator(__niter_base(__it.base())); }

    template<typename _Iterator, typename _Self = __niter_base_overloads>
      _GLIBCXX20_CONSTEXPR
      static auto
      __niter_base(move_iterator<_Iterator> __it)
      -> decltype(make_move_iterator(_Self::__niter_base(__it.base())))
      { return make_move_iterator(__niter_base(__it.base())); }

    ...
  };

> 
> As a further change in this area, I think it would be possible (and
> maybe nice) to remove __miter_base and replace the uses of it in
> std::move_backward(I,I,O) and std::move(I,I,O). That's left for another
> day.
> 
> Tested x86_64-linux.
> 
> -- >8 --
> 
> Move the functions for unwrapping and rewrapping __normal_iterator
> objects to the same file as the definition of __normal_iterator itself.
> 
> This will allow a later commit to make use of std::__niter_base in other
> headers without having to include all of <bits/stl_algobase.h>.
> 
> libstdc++-v3/ChangeLog:
> 
>       * include/bits/stl_algobase.h (__niter_base, __niter_wrap): Move
>       to ...
>       * include/bits/stl_iterator.h: ... here.
>       (__niter_base, __miter_base): Move all overloads to the end of
>       the header.
>       * testsuite/24_iterators/normal_iterator/wrapping.cc: New test.
> ---
>  libstdc++-v3/include/bits/stl_algobase.h      |  45 ------
>  libstdc++-v3/include/bits/stl_iterator.h      | 138 +++++++++++++-----
>  .../24_iterators/normal_iterator/wrapping.cc  |  29 ++++
>  3 files changed, 132 insertions(+), 80 deletions(-)
>  create mode 100644 
> libstdc++-v3/testsuite/24_iterators/normal_iterator/wrapping.cc
> 
> diff --git a/libstdc++-v3/include/bits/stl_algobase.h 
> b/libstdc++-v3/include/bits/stl_algobase.h
> index 384e5fdcdc9..751b7ad119b 100644
> --- a/libstdc++-v3/include/bits/stl_algobase.h
> +++ b/libstdc++-v3/include/bits/stl_algobase.h
> @@ -308,51 +308,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        return __a;
>      }
>  
> -  // Fallback implementation of the function in bits/stl_iterator.h used to
> -  // remove the __normal_iterator wrapper. See copy, fill, ...
> -  template<typename _Iterator>
> -    _GLIBCXX20_CONSTEXPR
> -    inline _Iterator
> -    __niter_base(_Iterator __it)
> -    
> _GLIBCXX_NOEXCEPT_IF(std::is_nothrow_copy_constructible<_Iterator>::value)
> -    { return __it; }
> -
> -#if __cplusplus < 201103L
> -  template<typename _Ite, typename _Seq>
> -    _Ite
> -    __niter_base(const ::__gnu_debug::_Safe_iterator<_Ite, _Seq,
> -              std::random_access_iterator_tag>&);
> -
> - template<typename _Ite, typename _Cont, typename _Seq>
> -    _Ite
> -    __niter_base(const ::__gnu_debug::_Safe_iterator<
> -              ::__gnu_cxx::__normal_iterator<_Ite, _Cont>, _Seq,
> -              std::random_access_iterator_tag>&);
> -#else
> -  template<typename _Ite, typename _Seq>
> -    _GLIBCXX20_CONSTEXPR
> -    decltype(std::__niter_base(std::declval<_Ite>()))
> -    __niter_base(const ::__gnu_debug::_Safe_iterator<_Ite, _Seq,
> -              std::random_access_iterator_tag>&)
> -    noexcept(std::is_nothrow_copy_constructible<_Ite>::value);
> -#endif
> -
> -  // Reverse the __niter_base transformation to get a
> -  // __normal_iterator back again (this assumes that __normal_iterator
> -  // is only used to wrap random access iterators, like pointers).
> -  template<typename _From, typename _To>
> -    _GLIBCXX20_CONSTEXPR
> -    inline _From
> -    __niter_wrap(_From __from, _To __res)
> -    { return __from + (std::__niter_base(__res) - 
> std::__niter_base(__from)); }
> -
> -  // No need to wrap, iterator already has the right type.
> -  template<typename _Iterator>
> -    _GLIBCXX20_CONSTEXPR
> -    inline _Iterator
> -    __niter_wrap(const _Iterator&, _Iterator __res)
> -    { return __res; }
> -
>    // All of these auxiliary structs serve two purposes.  (1) Replace
>    // calls to copy with memmove whenever possible.  (Memmove, not memcpy,
>    // because the input and output ranges are permitted to overlap.)
> diff --git a/libstdc++-v3/include/bits/stl_iterator.h 
> b/libstdc++-v3/include/bits/stl_iterator.h
> index 28a600c81cb..be3fa6f7a34 100644
> --- a/libstdc++-v3/include/bits/stl_iterator.h
> +++ b/libstdc++-v3/include/bits/stl_iterator.h
> @@ -654,24 +654,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>  #  endif // C++20
>  # endif // __glibcxx_make_reverse_iterator
>  
> -  template<typename _Iterator>
> -    _GLIBCXX20_CONSTEXPR
> -    auto
> -    __niter_base(reverse_iterator<_Iterator> __it)
> -    -> decltype(__make_reverse_iterator(__niter_base(__it.base())))
> -    { return __make_reverse_iterator(__niter_base(__it.base())); }
> -
>    template<typename _Iterator>
>      struct __is_move_iterator<reverse_iterator<_Iterator> >
>        : __is_move_iterator<_Iterator>
>      { };
> -
> -  template<typename _Iterator>
> -    _GLIBCXX20_CONSTEXPR
> -    auto
> -    __miter_base(reverse_iterator<_Iterator> __it)
> -    -> decltype(__make_reverse_iterator(__miter_base(__it.base())))
> -    { return __make_reverse_iterator(__miter_base(__it.base())); }
>  #endif // C++11
>  
>    // 24.4.2.2.1 back_insert_iterator
> @@ -1336,19 +1322,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      { return __normal_iterator<_Iterator, _Container>(__i.base() + __n); }
>  
>  _GLIBCXX_END_NAMESPACE_VERSION
> -} // namespace
> +} // namespace __gnu_cxx
>  
>  namespace std _GLIBCXX_VISIBILITY(default)
>  {
>  _GLIBCXX_BEGIN_NAMESPACE_VERSION
>  
> -  template<typename _Iterator, typename _Container>
> -    _GLIBCXX20_CONSTEXPR
> -    _Iterator
> -    __niter_base(__gnu_cxx::__normal_iterator<_Iterator, _Container> __it)
> -    
> _GLIBCXX_NOEXCEPT_IF(std::is_nothrow_copy_constructible<_Iterator>::value)
> -    { return __it.base(); }
> -
>  #if __cplusplus >= 201103L && __cplusplus <= 201703L
>    // Need to overload __to_address because the pointer_traits primary 
> template
>    // will deduce element_type of __normal_iterator<T*, C> as T* rather than 
> T.
> @@ -1820,13 +1799,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      __make_move_if_noexcept_iterator(_Tp* __i)
>      { return _ReturnType(__i); }
>  
> -  template<typename _Iterator>
> -    _GLIBCXX20_CONSTEXPR
> -    auto
> -    __niter_base(move_iterator<_Iterator> __it)
> -    -> decltype(make_move_iterator(__niter_base(__it.base())))
> -    { return make_move_iterator(__niter_base(__it.base())); }
> -
>    template<typename _Iterator>
>      struct __is_move_iterator<move_iterator<_Iterator> >
>      {
> @@ -1834,12 +1806,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        typedef __true_type __type;
>      };
>  
> -  template<typename _Iterator>
> -    _GLIBCXX20_CONSTEXPR
> -    auto
> -    __miter_base(move_iterator<_Iterator> __it)
> -    -> decltype(__miter_base(__it.base()))
> -    { return __miter_base(__it.base()); }
>  
>  #define _GLIBCXX_MAKE_MOVE_ITERATOR(_Iter) std::make_move_iterator(_Iter)
>  #define _GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(_Iter) \
> @@ -2985,6 +2951,108 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>  
>    /// @} group iterators
>  
> +_GLIBCXX_END_NAMESPACE_VERSION
> +} // namespace std
> +
> +namespace __gnu_debug
> +{
> +  template<typename _Iterator, typename _Sequence, typename _Category>
> +    class _Safe_iterator;
> +}
> +
> +namespace std _GLIBCXX_VISIBILITY(default)
> +{
> +_GLIBCXX_BEGIN_NAMESPACE_VERSION
> +  /// @cond undocumented
> +
> +  // Unwrap a __normal_iterator to get the underlying iterator
> +  // (usually a pointer). See uses in std::copy, std::fill, etc.
> +  template<typename _Iterator, typename _Container>
> +    _GLIBCXX20_CONSTEXPR
> +    _Iterator
> +    __niter_base(__gnu_cxx::__normal_iterator<_Iterator, _Container> __it)
> +    
> _GLIBCXX_NOEXCEPT_IF(std::is_nothrow_copy_constructible<_Iterator>::value)
> +    { return __it.base(); }
> +
> +  // Fallback implementation used for iterators that can't be unwrapped.
> +  template<typename _Iterator>
> +    _GLIBCXX20_CONSTEXPR
> +    inline _Iterator
> +    __niter_base(_Iterator __it)
> +    
> _GLIBCXX_NOEXCEPT_IF(std::is_nothrow_copy_constructible<_Iterator>::value)
> +    { return __it; }
> +
> +  // Overload for _Safe_iterator needs to be declared before uses of
> +  // std::__niter_base because we call it qualified so isn't found by ADL.
> +#if __cplusplus < 201103L
> +  template<typename _Ite, typename _Seq>
> +    _Ite
> +    __niter_base(const ::__gnu_debug::_Safe_iterator<_Ite, _Seq,
> +              std::random_access_iterator_tag>&);
> +
> + template<typename _Ite, typename _Cont, typename _Seq>
> +    _Ite
> +    __niter_base(const ::__gnu_debug::_Safe_iterator<
> +              ::__gnu_cxx::__normal_iterator<_Ite, _Cont>, _Seq,
> +              std::random_access_iterator_tag>&);
> +#else
> +  template<typename _Ite, typename _Seq>
> +    _GLIBCXX20_CONSTEXPR
> +    decltype(std::__niter_base(std::declval<_Ite>()))
> +    __niter_base(const ::__gnu_debug::_Safe_iterator<_Ite, _Seq,
> +              std::random_access_iterator_tag>&)
> +    noexcept(std::is_nothrow_copy_constructible<_Ite>::value);
> +#endif
> +
> +#if __cplusplus >= 201103L
> +  template<typename _Iterator>
> +    _GLIBCXX20_CONSTEXPR
> +    auto
> +    __niter_base(reverse_iterator<_Iterator> __it)
> +    -> decltype(__make_reverse_iterator(__niter_base(__it.base())))
> +    { return __make_reverse_iterator(__niter_base(__it.base())); }
> +
> +  template<typename _Iterator>
> +    _GLIBCXX20_CONSTEXPR
> +    auto
> +    __niter_base(move_iterator<_Iterator> __it)
> +    -> decltype(make_move_iterator(__niter_base(__it.base())))
> +    { return make_move_iterator(__niter_base(__it.base())); }
> +
> +  template<typename _Iterator>
> +    _GLIBCXX20_CONSTEXPR
> +    auto
> +    __miter_base(reverse_iterator<_Iterator> __it)
> +    -> decltype(__make_reverse_iterator(__miter_base(__it.base())))
> +    { return __make_reverse_iterator(__miter_base(__it.base())); }
> +
> +  template<typename _Iterator>
> +    _GLIBCXX20_CONSTEXPR
> +    auto
> +    __miter_base(move_iterator<_Iterator> __it)
> +    -> decltype(__miter_base(__it.base()))
> +    { return __miter_base(__it.base()); }
> +#endif
> +
> +  // Reverse the __niter_base transformation to get a __normal_iterator
> +  // back again (this assumes that __normal_iterator is only used to wrap
> +  // random access iterators, like pointers).
> +  // All overloads of std::__niter_base must be declared before this.
> +  template<typename _From, typename _To>
> +    _GLIBCXX20_CONSTEXPR
> +    inline _From
> +    __niter_wrap(_From __from, _To __res)
> +    { return __from + (std::__niter_base(__res) - 
> std::__niter_base(__from)); }
> +
> +  // No need to wrap, iterator already has the right type.
> +  template<typename _Iterator>
> +    _GLIBCXX20_CONSTEXPR
> +    inline _Iterator
> +    __niter_wrap(const _Iterator&, _Iterator __res)
> +    { return __res; }
> +
> +  /// @endcond
> +
>  #if __cpp_deduction_guides >= 201606
>    // These helper traits are used for deduction guides
>    // of associative containers.
> diff --git a/libstdc++-v3/testsuite/24_iterators/normal_iterator/wrapping.cc 
> b/libstdc++-v3/testsuite/24_iterators/normal_iterator/wrapping.cc
> new file mode 100644
> index 00000000000..bbfd4264a36
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/24_iterators/normal_iterator/wrapping.cc
> @@ -0,0 +1,29 @@
> +#include <iterator>
> +#include <algorithm>
> +#include <vector>
> +#ifndef _GLIBCXX_DEBUG
> +#include <debug/vector>
> +#endif
> +
> +struct S { };
> +
> +int main()
> +{
> +  S s[1];
> +  std::vector<S> v(1);
> +  std::copy(s, s, v.rbegin());
> +#if __cplusplus >= 201103L
> +  std::copy(s, s, std::make_move_iterator(v.begin()));
> +  std::copy(s, s, std::make_move_iterator(v.rbegin()));
> +#endif
> +
> +#ifndef _GLIBCXX_DEBUG
> +  __gnu_debug::vector<S> dv(1);
> +  std::copy(s, s, dv.rbegin());
> +#if __cplusplus >= 201103L
> +  std::copy(s, s, std::make_move_iterator(dv.begin()));
> +  std::copy(s, s, std::make_move_iterator(dv.rbegin()));
> +#endif
> +#endif
> +}
> +
> -- 
> 2.46.2
> 
> 

Reply via email to