Search code examples
c++boostboost-range

Partition a boost::range::transformed range adaptor


I am trying to partition a boost::range::transformed adaptor:

#include <boost/range.hpp>
#include <boost/range/algorithm/partition.hpp>
#include <boost/range/algorithm/transform.hpp>
#include <boost/range/any_range.hpp>
#include <boost/range/adaptor/transformed.hpp>

using boost::adaptors::transformed;

void foo()
{
    boost::any_range<int,
                     boost::forward_traversal_tag,
                     int&,
                     std::ptrdiff_t> r;

    auto t = r | transformed( [](int) {return 0;} );
    auto p = boost::range::partition(t, [](int) {return true;} );

    std::vector<int> v;

    auto t2 = r | transformed( [](int) {return 0;} );
    auto p2 = boost::range::partition(t2, [](int) {return true;} );
}

The compiler complains for p1 and p2 that it cannot find a function __partition which accepts a boost::iterators__transform_iterator. The complete error message is

In file included from /opt/rh/devtoolset-3/root/usr/include/c++/4.9.2/algorithm:62:0,
                 from /tmp/boost_1_59_0/boost/iterator/iterator_concepts.hpp:29,
                 from /tmp/boost_1_59_0/boost/range/concepts.hpp:20,
                 from /tmp/boost_1_59_0/boost/range/size_type.hpp:20,
                 from /tmp/boost_1_59_0/boost/range/size.hpp:21,
                 from /tmp/boost_1_59_0/boost/range/functions.hpp:20,
                 from /tmp/boost_1_59_0/boost/range.hpp:18,
                 from range_partition.cpp:1:
/opt/rh/devtoolset-3/root/usr/include/c++/4.9.2/bits/stl_algo.h: In instantiation of ‘_BIter std::partition(_BIter, _BIter, _Predicate) [with _BIter = boost::iterators::transform_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<foo()::<lambda(int)>, int>, boost::range_detail::any_iterator<int, boost::iterators::forward_traversal_tag, int&, long int, boost::any_iterator_buffer<64ul> >, boost::iterators::use_default, boost::iterators::use_default>; _Predicate = foo()::<lambda(int)>]’:
/tmp/boost_1_59_0/boost/range/algorithm/partition.hpp:34:65:   required from ‘typename boost::range_iterator<C>::type boost::range::partition(ForwardRange&, UnaryPredicate) [with ForwardRange = boost::range_detail::transformed_range<foo()::<lambda(int)>, boost::range_detail::any_range<int, boost::iterators::forward_traversal_tag, int&, long int> >; UnaryPredicate = foo()::<lambda(int)>; typename boost::range_iterator<C>::type = boost::iterators::transform_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<foo()::<lambda(int)>, int>, boost::range_detail::any_iterator<int, boost::iterators::forward_traversal_tag, int&, long int, boost::any_iterator_buffer<64ul> >, boost::iterators::use_default, boost::iterators::use_default>]’
range_partition.cpp:22:66:   required from here
/opt/rh/devtoolset-3/root/usr/include/c++/4.9.2/bits/stl_algo.h:4509:43: error: no matching function for call to ‘__partition(boost::iterators::transform_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<foo()::<lambda(int)>, int>, boost::range_detail::any_iterator<int, boost::iterators::forward_traversal_tag, int&, long int, boost::any_iterator_buffer<64ul> >, boost::iterators::use_default, boost::iterators::use_default>&, boost::iterators::transform_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<foo()::<lambda(int)>, int>, boost::range_detail::any_iterator<int, boost::iterators::forward_traversal_tag, int&, long int, boost::any_iterator_buffer<64ul> >, boost::iterators::use_default, boost::iterators::use_default>&, foo()::<lambda(int)>&, std::__iterator_traits<boost::iterators::transform_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<foo()::<lambda(int)>, int>, boost::range_detail::any_iterator<int, boost::iterators::forward_traversal_tag, int&, long int, boost::any_iterator_buffer<64ul> >, boost::iterators::use_default, boost::iterators::use_default>, true>::iterator_category)’
          std::__iterator_category(__first));
                                           ^
/opt/rh/devtoolset-3/root/usr/include/c++/4.9.2/bits/stl_algo.h:4509:43: note: candidates are:
/opt/rh/devtoolset-3/root/usr/include/c++/4.9.2/bits/stl_algo.h:1462:5: note: template<class _ForwardIterator, class _Predicate> _ForwardIterator std::__partition(_ForwardIterator, _ForwardIterator, _Predicate, std::forward_iterator_tag)
     __partition(_ForwardIterator __first, _ForwardIterator __last,
     ^
/opt/rh/devtoolset-3/root/usr/include/c++/4.9.2/bits/stl_algo.h:1462:5: note:   template argument deduction/substitution failed:
/opt/rh/devtoolset-3/root/usr/include/c++/4.9.2/bits/stl_algo.h:4509:43: note:   cannot convert ‘std::__iterator_category<boost::iterators::transform_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<foo()::<lambda(int)>, int>, boost::range_detail::any_iterator<int, boost::iterators::forward_traversal_tag, int&, long int, boost::any_iterator_buffer<64ul> >, boost::iterators::use_default, boost::iterators::use_default> >((*(const boost::iterators::transform_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<foo()::<lambda(int)>, int>, boost::range_detail::any_iterator<int, boost::iterators::forward_traversal_tag, int&, long int, boost::any_iterator_buffer<64ul> >, boost::iterators::use_default, boost::iterators::use_default>*)(& __first)))’ (type ‘std::__iterator_traits<boost::iterators::transform_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<foo()::<lambda(int)>, int>, boost::range_detail::any_iterator<int, boost::iterators::forward_traversal_tag, int&, long int, boost::any_iterator_buffer<64ul> >, boost::iterators::use_default, boost::iterators::use_default>, true>::iterator_category {aka boost::iterators::detail::iterator_category_with_traversal<std::input_iterator_tag, boost::iterators::forward_traversal_tag>}’) to type ‘std::forward_iterator_tag’
          std::__iterator_category(__first));
                                           ^
/opt/rh/devtoolset-3/root/usr/include/c++/4.9.2/bits/stl_algo.h:1487:5: note: template<class _BidirectionalIterator, class _Predicate> _BidirectionalIterator std::__partition(_BidirectionalIterator, _BidirectionalIterator, _Predicate, std::bidirectional_iterator_tag)
     __partition(_BidirectionalIterator __first, _BidirectionalIterator __last,
     ^
/opt/rh/devtoolset-3/root/usr/include/c++/4.9.2/bits/stl_algo.h:1487:5: note:   template argument deduction/substitution failed:
/opt/rh/devtoolset-3/root/usr/include/c++/4.9.2/bits/stl_algo.h:4509:43: note:   cannot convert ‘std::__iterator_category<boost::iterators::transform_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<foo()::<lambda(int)>, int>, boost::range_detail::any_iterator<int, boost::iterators::forward_traversal_tag, int&, long int, boost::any_iterator_buffer<64ul> >, boost::iterators::use_default, boost::iterators::use_default> >((*(const boost::iterators::transform_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<foo()::<lambda(int)>, int>, boost::range_detail::any_iterator<int, boost::iterators::forward_traversal_tag, int&, long int, boost::any_iterator_buffer<64ul> >, boost::iterators::use_default, boost::iterators::use_default>*)(& __first)))’ (type ‘std::__iterator_traits<boost::iterators::transform_iterator<boost::range_detail::default_constructible_unary_fn_wrapper<foo()::<lambda(int)>, int>, boost::range_detail::any_iterator<int, boost::iterators::forward_traversal_tag, int&, long int, boost::any_iterator_buffer<64ul> >, boost::iterators::use_default, boost::iterators::use_default>, true>::iterator_category {aka boost::iterators::detail::iterator_category_with_traversal<std::input_iterator_tag, boost::iterators::forward_traversal_tag>}’) to type ‘std::bidirectional_iterator_tag’
          std::__iterator_category(__first));

I am quite lost and don't see a solution for this. Any help would be greatly appreciated.


Solution

  • The transformed range is not a mutable sequence.

    You cannot partition such a range (because you can't swap elements).

    You can look at partition_copy, in which case you should have a destination iterator to receive the partitioned range.

    With a bit of work, it seems like you should be able to craft a Boost Range style Adaptor out of this too (see docs on extending: http://www.boost.org/doc/libs/1_59_0/libs/range/doc/html/range/reference/extending/method_3.html) in case you really insist on having partitioned like in the first example.

    Sample

    Here's what I'd suggest as a 'halfway' solution: convenient without too much effort (c++11)

    Live On Coliru

    #include <boost/range.hpp>
    #include <boost/range/algorithm.hpp>
    #include <boost/range/algorithm_ext.hpp>
    #include <boost/range/any_range.hpp>
    #include <boost/range/adaptors.hpp>
    
    using boost::adaptors::transformed;
    
    namespace {
        // convenience wrapper
        template <typename Range, typename Predicate, typename OutIt1, typename OutIt2>
            std::pair<OutIt1, OutIt2> partition_copy(Range const& range, OutIt1 out1, OutIt2 out2, Predicate&& predicate) {
                return std::partition_copy(
                        boost::begin(range), boost::end(range),
                        out1, out2,
                        std::forward<Predicate>(predicate));
            }
    }
    
    #include <iostream>
    
    int main()
    {
        std::vector<int> demo { 1,2,7,3,-9,42 };
    
        std::vector<int> even, odd;
        partition_copy(
                demo | transformed( [](int i) {return i+1;} ), 
                back_inserter(even), back_inserter(odd),
                [] (int i) { return 0 == i%2; }
            );
    
        // output
        boost::copy(even, std::ostream_iterator<int>(std::cout << "even : ", " "));
        boost::copy(odd,  std::ostream_iterator<int>(std::cout << "\nodd  : ", " "));
    }
    

    Prints

    even : 2 8 4 -8 
    odd  : 3 43