2

I would like to concatenate ranges returned by function into one big range.Consider following code:

some_type_i_cant_figure_out bar() {
    typedef std::vector<int>::const_iterator iter;
    std::vector<int> aaa;
    /* fill some data into aaa*/
    some_type_i_cant_figure_out cc;
    for (int i = 0; i < aaa.size(); ++i) {
    std::pair<iter, iter> bbb = foo(aaa, i);
    ccc = boost::join(ccc, bbb);
    }
    return ccc;
}

What I'm trying to achieve:
The aaa vector is huge and foo may return quite big ranges. Of course I can just create copies of all elements in range into new vector of integers and return it. It is inefficient, wasting memory and time. So I would like return one boost::joined_range. In worst case, I can live with vector of ranges, but it would be too simple and not that elegant :) besides the joined_range isnt default constructible (which is a problematic for this example implementation) what would be the return value type? the temp variable (ccc) type and what would be the correct and elegant way to achieve the above?

kreuzerkrieg
  • 3,009
  • 3
  • 28
  • 59

1 Answers1

1

First off, the end result of your code would appear to be similar to just

auto cc(aaa);
boost::stable_sort(cc);

(Assuming, from your sample code, that aaa contains integers in the range [0..size()-1))

If you can afford to simply copy, just use a backinsert iterator:

std::vector<int> cc;
for (size_t i = 0; i < aaa.size(); ++i)
    boost::copy(boost::equal_range(aaa, i), back_inserter(cc));

Otherwise, you can hide the accumulating joins by using any_range:

boost::any_range<int, boost::forward_traversal_tag, int> r;
for (size_t i = 0; i < aaa.size(); ++i)
    r = boost::join(r, boost::equal_range(aaa, i));

Live On Coliru

#include <boost/range/any_range.hpp>
#include <boost/range/join.hpp>
#include <boost/range/algorithm.hpp>
#include <iostream>

int main() {
    std::vector<int> const aaa { 1,1,1,4,5,5,9,42,42,42,42,42,42 };

    boost::any_range<int, boost::forward_traversal_tag, int> r;
    for (size_t i = 0; i < aaa.size(); ++i)
        r = boost::join(r, boost::equal_range(aaa, i));

    boost::copy(r, std::ostream_iterator<int>(std::cout << "result: ", " "));
}

Prints

result: 1 1 1 4 5 5 9
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Anyways, I think this should cover it :) – sehe Nov 19 '14 at 09:17
  • Sorry, my oversimplified example has deceived you. I've edited the question. – kreuzerkrieg Nov 19 '14 at 09:23
  • So, replace `boost::equal_range(aaa,i)` with `foo(aaa,i)` and you're done :) – sehe Nov 19 '14 at 09:24
  • Tadaaaa! boost::any_range is the answer! – kreuzerkrieg Nov 19 '14 at 09:42
  • 1
    @kreuzerkrieg Beware of the simplicity: I imagine `any_range` works its magic using erasure, which means that you still (logically) have a sequence of ranges, except now it is likely stored in a recursive tree of range objects, each node potentially adding a virtual function call, and possibly dynamic allocation. _(The virtual call **might** be optimized by a very smart compiler, and the dynamic allocation might use small-object-buffer optimization small ranges (as a QoI concern), but that still limits the composability of the ranges.)_ [...] – sehe Nov 19 '14 at 09:52
  • 1
    [..] I'd say, profile it! And I strongly suspect you'll find that a list of `iterator_range` is actually the best solution for any significant scale (it expresses intent and fits the purpose...) – sehe Nov 19 '14 at 09:53
  • Correct, it uses type erasure and there is a performance penalty. and of course I will profile overall performance once I finish overhauling all the mess I'm working on – kreuzerkrieg Nov 19 '14 at 10:22