[PATCH 8/8] libstdc++: Directly implement ranges::shuffle [PR100795]

Jonathan Wakely jwakely@redhat.com
Fri Jun 27 13:59:20 GMT 2025


On 26/06/25 22:25 -0400, Patrick Palka wrote:
>	PR libstdc++/100795

OK for trunk.

>
>libstdc++-v3/ChangeLog:
>
>	* include/bits/ranges_algo.h (shuffle_fn::operator()):
>	Reimplement directly.
>	* testsuite/25_algorithms/shuffle/constrained.cc (test02):
>---
> libstdc++-v3/include/bits/ranges_algo.h       | 58 ++++++++++++++++++-
> .../25_algorithms/shuffle/constrained.cc      | 25 ++++++++
> 2 files changed, 80 insertions(+), 3 deletions(-)
>
>diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h
>index 672a0ebce0de..83eaa7da28b9 100644
>--- a/libstdc++-v3/include/bits/ranges_algo.h
>+++ b/libstdc++-v3/include/bits/ranges_algo.h
>@@ -1952,9 +1952,61 @@ namespace ranges
>       _Iter
>       operator()(_Iter __first, _Sent __last, _Gen&& __g) const
>       {
>-	auto __lasti = ranges::next(__first, __last);
>-	std::shuffle(std::move(__first), __lasti, std::forward<_Gen>(__g));
>-	return __lasti;
>+	// FIXME: Correctly handle integer-class difference types.
>+	if (__first == __last)
>+	  return __first;
>+
>+	using _DistanceType = iter_difference_t<_Iter>;
>+	using __ud_type = __detail::__make_unsigned_like_t<_DistanceType>;
>+	using __distr_type = std::uniform_int_distribution<__ud_type>;
>+	using __p_type = typename __distr_type::param_type;
>+
>+	using __uc_type
>+	  = common_type_t<typename remove_reference_t<_Gen>::result_type, __ud_type>;
>+
>+	const __uc_type __urngrange = __g.max() - __g.min();
>+	const __uc_type __urange = __uc_type(__last - __first);
>+
>+	if (__urngrange / __urange >= __urange)
>+	  // I.e. (__urngrange >= __urange * __urange) but without wrap issues.
>+	  {
>+	    _Iter __i = __first + 1;
>+
>+	    // Since we know the range isn't empty, an even number of elements
>+	    // means an uneven number of elements /to swap/, in which case we
>+	    // do the first one up front:
>+
>+	    if ((__urange % 2) == 0)
>+	      {
>+		__distr_type __d{0, 1};
>+		ranges::iter_swap(__i++, __first + __d(__g));
>+	      }
>+
>+	    // Now we know that __last - __i is even, so we do the rest in pairs,
>+	    // using a single distribution invocation to produce swap positions
>+	    // for two successive elements at a time:
>+
>+	    while (__i != __last)
>+	      {
>+		const __uc_type __swap_range = __uc_type(__i - __first) + 1;
>+
>+		const pair<__uc_type, __uc_type> __pospos =
>+		  __gen_two_uniform_ints(__swap_range, __swap_range + 1, __g);
>+
>+		ranges::iter_swap(__i++, __first + __pospos.first);
>+		ranges::iter_swap(__i++, __first + __pospos.second);
>+	      }
>+
>+	    return __i;
>+	  }
>+
>+	__distr_type __d;
>+
>+	_Iter __i = __first + 1;
>+	for (; __i != __last; ++__i)
>+	  ranges::iter_swap(__i, __first + __d(__g, __p_type(0, __i - __first)));
>+
>+	return __i;
>       }
>
>     template<random_access_range _Range, typename _Gen>
>diff --git a/libstdc++-v3/testsuite/25_algorithms/shuffle/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/shuffle/constrained.cc
>index d0977a292fee..70c6bdfc3d9e 100644
>--- a/libstdc++-v3/testsuite/25_algorithms/shuffle/constrained.cc
>+++ b/libstdc++-v3/testsuite/25_algorithms/shuffle/constrained.cc
>@@ -20,6 +20,7 @@
>
> #include <algorithm>
> #include <random>
>+#include <ranges>
> #include <vector>
> #include <testsuite_hooks.h>
> #include <testsuite_iterators.h>
>@@ -62,8 +63,32 @@ test01()
>     }
> }
>
>+void
>+test02()
>+{
>+  // PR libstdc++/100795 - ranges::shuffle should not use std::shuffle directly
>+#if 0 // FIXME: ranges::shuffle rejects integer-class difference types.
>+#if __SIZEOF_INT128__
>+  auto v = std::views::iota(__int128(0), __int128(20));
>+#else
>+  auto v = std::views::iota(0ll, 20ll);
>+#endif
>+#else
>+  auto v = std::views::iota(0, 20);
>+#endif
>+
>+  int storage[20] = {2,5,4,3,1,6,7,9,10,8,11,14,12,13,15,16,18,0,19,17};
>+  auto w = v | std::views::transform([&](auto i) -> int& { return storage[i]; });
>+  using type = decltype(w);
>+  static_assert( std::ranges::random_access_range<type> );
>+
>+  std::ranlux48_base g;
>+  ranges::shuffle(w, g);
>+}
>+
> int
> main()
> {
>   test01();
>+  test02();
> }
>-- 
>2.50.0.131.gcf6f63ea6b
>
>



More information about the Libstdc++ mailing list