Conditional boost::range::join

72 views Asked by At

I want to write a function like this:

template<class IterableType>
void CheckAndProcessIterables(IterableType& a, IterableType& b, IterableType& c) {
  IteratorRangeType range{}; // empty range
  if (Check(a)) {
    range = boost::range::join(range, a);
  }
  if (Check(b)) {
    range = boost::range::join(range, b);
  }
  if (Check(c)) {
    range = boost::range::join(range, c);
  }
  Process(range);
}

Is it possible? Which type should I use instead of IteratorRangeType? As far as I understand it, boost::range::join return type depends on it's arguments. Is there some wrapper class which can be assigned any type of range as long as its underlying value type is same?

1

There are 1 answers

4
sehe On BEST ANSWER

You can use type erased iterator ranges, which Boost has in the form of any_range.

Beware of the performance cost of these, which can quickly become very noticable. I'd rethink the approach unless you're very sure that this not on any hot path and readability is a much more of a concern than performance.

Live On CoCompiler Explorer

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

// for demo only:
#include <boost/range/algorithm/for_each.hpp>
#include <boost/lambda/lambda.hpp>
#include <fmt/ranges.h>

template<class Range>
bool Check(Range const& r) {
    bool odd_len = boost::size(r) % 2;
    fmt::print("Check {}, odd_len? {}\n", r, odd_len);
    return odd_len;
}

template<class Range>
void Process(Range const& r) {
    fmt::print("Processing {}\n", r);
    using namespace boost::lambda;
    for_each(r, _1 *= _1);
}

template<class IterableType>
void CheckAndProcessIterables(IterableType& a, IterableType& b, IterableType& c) {
    using V = typename boost::range_value<IterableType>::type;
    using ErasedRange= boost::any_range<V, boost::forward_traversal_tag>;

    ErasedRange range{}; // empty range
    if (Check(a)) {
        range = boost::range::join(range, a);
    }
    if (Check(b)) {
        range = boost::range::join(range, b);
    }
    if (Check(c)) {
        range = boost::range::join(range, c);
    }
    Process(range);
}

int main() {
    std::vector a{1, 2, 3}, b{4, 5}, c{6, 7, 8};
    CheckAndProcessIterables(a, b, c);

    fmt::print("After process: a:{} b:{} c:{}\n", a, b, c);
}

Prints

Check {1, 2, 3}, odd_len? true
Check {4, 5}, odd_len? false
Check {6, 7, 8}, odd_len? true
Processing {1, 2, 3, 6, 7, 8}
After process: a:{1, 4, 9} b:{4, 5} c:{36, 49, 64}