c++boostboost-range

Conditional boost::range::join


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?


Solution

  • 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}