1

Here is an example of a range adaptor based off of Implement a Range Adaptor with arguments:

#include <boost/range/join.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/iterator/permutation_iterator.hpp>
#include <vector>
#include <list>
#include <iterator>
#include <iostream>

template <typename Range, typename Index>
class permutation_range :
        public boost::iterator_range<
                boost::permutation_iterator<
                        typename boost::range_iterator<Range>::type,
                        typename boost::range_iterator<Index>::type>>
{
    using value_type = typename boost::range_value<Range>::type;
    using replaced_iterator = boost::permutation_iterator<
                            typename boost::range_iterator<Range>::type,
                            typename boost::range_iterator<Index>::type>;
    using base_t = boost::iterator_range<replaced_iterator>;
public:
    permutation_range(Range& r, Index& i)
            : base_t(replaced_iterator(boost::begin(r), boost::begin(i)),
                    replaced_iterator(boost::end(r), boost::end(i)))
    {}
};

template <typename Range, typename Index>
permutation_range<Range, Index>
permutation(Range& r, Index& i)
{
    return permutation_range<Range, Index>(r, i);
}

int main()
{
    std::vector<int> v1{99, 1, 99,  2, 99, 3};
    std::list<int> indexer{1, 3, 5};
    boost::copy(permutation(v1, indexer),
                std::ostream_iterator<int>(std::cout, " "));
}

Output

1 2 3

I want to adapt the above to use boost::joined_range. In other words, take two vectors and then join them into one longer range inside of permutation_range. The idea is simple:

Example which would output 2 4 6:

int main()
{
    std::vector<int> v1{1, 2, 3};
    std::vector<int> v2{4, 5, 6};
    std::list<int> indexer{1, 3, 5};
    boost::copy(permutation(v1, v2, indexer),
                std::ostream_iterator<int>(std::cout, " "));
}

All my attempts so far have ended in a huge list of compiler errors, I haven't showed the attempts because I may be way off. I don't know if it is possible, but could anyone help me out here?

FINAL EDIT (Solution with a little hack to get the range initialized before the base class)

#include <boost/range/join.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/iterator/permutation_iterator.hpp>
#include <vector>
#include <list>
#include <iterator>
#include <iostream>

template <typename R1, typename R2>
struct initialize_me_first
{
    initialize_me_first(typename boost::range::joined_range<const R1, const R2> j)
    : j_range(j)
    {}
    typename boost::range::joined_range<const R1, const R2> j_range;
};

template <typename Range1, typename Range2, typename Index>
class permutation_range :
        public initialize_me_first<Range1, Range2>,
        public boost::iterator_range<
                boost::permutation_iterator<
                        typename boost::range_iterator<
                            boost::range::joined_range<Range1, Range2>>::type,
                        typename boost::range_iterator<Index>::type>>
{
    using value_type = typename boost::range_value<Range1>::type;
    using replaced_iterator = boost::permutation_iterator<
                    typename boost::range_iterator<
                            boost::range::joined_range<Range1, Range2>>::type,
                            typename boost::range_iterator<Index>::type>;
    using base_t = boost::iterator_range<replaced_iterator>;
    using init = initialize_me_first<Range1, Range2>;
public:
    permutation_range(const Range1& r1, const Range2& r2, const Index& i)
            : init(boost::join(r1, r2)),
              base_t(replaced_iterator(boost::begin(init::j_range),
                                       boost::begin(i)),
                    replaced_iterator(boost::end(init::j_range),
                                       boost::end(i)))
    {}
};

template <typename Range1, typename Range2, typename Index>
permutation_range<const Range1, const Range2, const Index>
permutation(const Range1& r1, const Range2& r2, const Index& i)
{
    return permutation_range<const Range1,
                             const Range2,
                             const Index>(r1, r2, i);
}

int main()
{
    std::vector<int> v1{1, 2, 3};
    std::vector<int> v2{4, 5, 6};
    std::list<int> indexer{1, 3, 5};
    boost::copy(permutation(v1, v2, indexer),
                std::ostream_iterator<int>(std::cout, " "));
}
TemplateRex
  • 69,038
  • 19
  • 164
  • 304
Jesse Good
  • 50,901
  • 14
  • 124
  • 166

1 Answers1

2

To join two ranges, use boost::join:

#include <boost/range/join.hpp>

boost::copy(permutation(boost::join(v1, v2), indexer), ...

But change your Range parameters to be passed by const reference instead of non-const reference. There's no reason to require modifiable versions of those arguments since you're not actually permuting their contents.

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • Thanks, I also realized I could stick `boost::join` inside of `permutation` if I wanted to keep the `permutation(v1, v2, indexer)` syntax. However, my original goal was to change `permutation_range` to use `boost::join` (don't know if that is possible though). – Jesse Good Jul 24 '12 at 22:13
  • That will be harder. Your `permutation_range` would need to have a copy of the joined range that it creates — it needs to pass the same value of `r` for both the `replaced_iterator` objects it creates. That range must exist *before* the base class is initialized. That's solvable, but you also have the added syntax complication of an effectively "optional" parameter (`v2`) appearing in the *middle* of the argument list. If the caller wants joined ranges, let the caller be responsible for that and keep your interface simple and focused on just one thing. – Rob Kennedy Jul 24 '12 at 22:21
  • Good point about the interface and thanks a lot for the help. After your comments, I was able to come up with something (see EDIT in question), however, how can I get the range to exist before the base class is initialized? – Jesse Good Jul 24 '12 at 23:13
  • I came up with a solution by inheriting from two classes (see FINAL EDIT). Thanks again! – Jesse Good Jul 25 '12 at 00:23