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
>
>