How to write a range-v3 action for random_shuffle?

Using the range-v3 library (by @EricNiebler) makes the algorithmic writing code much more compact, for example. here's how to create a bunch of random numbers:

#include <range/v3/all.hpp> #include <iostream> #include <vector> int main() { using namespace ranges; auto const N = 10; std::vector<int> v; v.reserve(N); v |= action::push_back(view::iota(0, N)); random_shuffle(v); copy(v, ostream_iterator<>(std::cout, ",")); } 

Living example.

However, I would prefer to extend the pipeline with a hypothetical action::random_shuffle() as follows

 v |= action::push_back(view::iota(0, N)) | action::random_shuffle(); 

Here is my attempt to write such an action (unfortunately, writing the new range-v3 code is a bit more complicated than using the library)

 #include <functional> // bind, placeholders::_1 namespace ranges { inline namespace v3 { /// \addtogroup group-actions /// @{ namespace action { struct random_shuffle_fn { private: friend action_access; static auto bind(random_shuffle_fn random_shuffle) RANGES_DECLTYPE_AUTO_RETURN ( std::bind(random_shuffle, std::placeholders::_1) ) template<typename Gen> static auto bind(random_shuffle_fn random_shuffle, Gen && rand) RANGES_DECLTYPE_AUTO_RETURN ( std::bind(random_shuffle, std::placeholders::_1, bind_forward<Gen>(rand)) ) public: struct ConceptImpl { template<typename Rng, typename I = range_iterator_t<Rng>> auto requires_(Rng&&) -> decltype( concepts::valid_expr( concepts::model_of<concepts::RandomAccessRange, Rng>(), concepts::is_true(Permutable<I>()) )); }; template<typename Rng> using Concept = concepts::models<ConceptImpl, Rng>; template<typename Rng, CONCEPT_REQUIRES_(Concept<Rng>())> Rng operator()(Rng && rng) const { ranges::random_shuffle(rng); return std::forward<Rng>(rng); } template<typename Rng, typename Gen, CONCEPT_REQUIRES_(Concept<Rng>())> Rng operator()(Rng && rng, Gen && rand) const { ranges::random_shuffle(rng, std::forward<Gen>(rand)); return std::forward<Rng>(rng); } #ifndef RANGES_DOXYGEN_INVOKED template<typename Rng> void operator()(Rng &&) const { CONCEPT_ASSERT_MSG(RandomAccessRange<Rng>(), "The object on which action::random_shuffle operates must be a model of the " "RandomAccessRange concept."); using I = range_iterator_t<Rng>; CONCEPT_ASSERT_MSG(Permutable<I>(), "The iterator type of the range passed to action::random_shuffle must allow its " "elements to be permuted; that is, the values must be movable and the " "iterator must be mutable."); } #endif }; /// \ingroup group-actions /// \relates sort_fn /// \sa `action` namespace { constexpr auto&& random_shuffle = static_const<action<random_shuffle_fn>>::value; } } /// @} } } 

A living example that cannot be compiled because some operator() deeply hidden is not found somewhere.

As far as I can see, I correctly passed the above code from a similar code, for example. action::sort() . The only difference is that random_shuffle() has two overloads (one accepts a random generator), while all other actions (including sort ) have one overload with default values ​​for their additional parameters (comparators, predicates, projectors, etc. ..). This means two static bind() functions from random_shuffle_fn above, while all other actions have only one bind() overload.

Question : how to write a range-v3 action for random_shuffle?

+6
source share
2 answers

You have two ambiguous overloads of random_shuffle_function::operator()(Rng&&) , your β€œerror hook” overload should be limited to only accept arguments that the proper overload rejects (we really need C ++ concepts, so I never again should be limited to SFINAE overloads):

 #ifndef RANGES_DOXYGEN_INVOKED template<typename Rng, CONCEPT_REQUIRES_(!Concept<Rng>())> void operator()(Rng &&) const { CONCEPT_ASSERT_MSG(RandomAccessRange<Rng>(), "The object on which action::random_shuffle operates must be a model of the " "RandomAccessRange concept."); using I = range_iterator_t<Rng>; CONCEPT_ASSERT_MSG(Permutable<I>(), "The iterator type of the range passed to action::random_shuffle must allow its " "elements to be permuted; that is, the values must be movable and the " "iterator must be mutable."); } #endif 

In addition, you need to skip the tube through action::random_shuffle :

 v |= action::push_back(view::iota(0, N)) | action::random_shuffle; 

Demo

+3
source

The latest version of git already contains action::shuffle . It can be used as follows:

 #include <random> std::mt19937 gen; ... v |= action::push_back(view::iota(0, N)) | action::shuffle(gen); 
+3
source

Source: https://habr.com/ru/post/987824/


All Articles