Boost logo

Boost :

Subject: [boost] [range][algorithms] How about making the range algorithms pipeable as the range adaptors are?
From: Viktor Sehr (viktor.sehr_at_[hidden])
Date: 2013-09-19 12:17:54


It would be syntactically really nice if the stl-algorithms wrapped in
boost range were pipeable, as with the adaptors, making it seamless to
combine them in left-to-right statements

For non-mutating algorithms I think it's an obvious evolvement
const auto& max_pos = some_range | filtered(..) | max_element;
const auto& num_elements = some_range | filtered(..) | count(42);

...instead of inside out as it is now
const auto& max_pos = max_element(some_range | filtered(..));
const auto& num_elements = count(some_range | filtered(..), 42);

For non-mutating algorithms it might not be an obvious solution, as the
range beneath is modified, but I still think it should be possible

boost::erase(my_vector | sort | unique);
...instead of
boost::erase(unique(sort(my_vector));

*Implementation example with std::accumulate:*

// Tag to be recognized by the global pipe operatortemplate <typename
value_type>struct accumulate_argument_tag {
  accumulate_argument_tag(const value_type& value) : value_(value) {}
  const value_type& value_;
};
 struct accumulate_tag {
  // Put the regular algorithm as an operator() function where the
first argument is the range
  template <typename range_type, typename value_type>
  value_type operator()(const range_type& range, const value_type& start) {
    return std::accumulate(std::begin(range), std::end(range), start);
  }
  // If no range is provided, return a tag which contains the
argument, which can then be identified by the global pipe operator
  template <typename value_type>
  accumulate_argument_tag<value_type>
  operator()(const value_type& value) {
    return accumulate_argument_tag<value_type>(value);
  }
};static accumulate_tag accumulate;
 // Global pipe operator which takes one argumenttemplate <typename
range_type, typename value_type>value_type
operator|(const range_type& range, const
accumulate_argument_tag<value_type>& argument_holder) {
  return std::accumulate(std::begin(range), std::end(range),
argument_holder.value_);
}
 // Global pipe operator which can accept 'accumulate' without any
parenthesis// (assuming that std::accumulate can be used without
arguments)template <typename range_type>typename
range_type::value_type
operator|(const range_type& range, const accumulate_tag&) {
  return std::accumulate(std::begin(range), std::end(range),
range_type::value_type(0));
}

// Usage
std::vector<int> vec;for(size_t i = 0; i < 135; ++i)
  vec.push_back(i);auto a = accumulate(vec, 0);auto b = vec |
accumulate(0);auto c = vec | accumulate;

/Viktor


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk