views:

55

answers:

1

I'm pondering a current limitation of STL iterators and wondering if there's an elegant way around it. Here's my situation: I have a class that encapsulates a sequence container and a generic method to mutate the contents of the container, for example:

class Container {
  typedef std::vector<int> Data;
  Data data_;
 public:
  template <class Mutator>
  ?? mutate() {
    Mutator m;
    return m(??);
  }
};

Some details have been elided for concision, but the main problem I'm considering is what to pass to the mutator (the ?? in the m() functor call). A secondary question is what to return from mutate to allow simple composition of mutators. One possibility is to pass (and return) a begin/end pair of iterators into data_, or even a boost::sub_range, for example:

template <class Mutator>
boost::sub_range<Data> mutate() {
  Mutator m;
  return m(boost::sub_range<Data>(data_);
}

This would allow me to do most of what I want on the data, such as negating all values in range, multiplying by a factor, etc. This also allows me chain, or compose, various mutators, as in this (shortened) version:

return m1(m2(m3(data_)));

Thus, any mutator can, for example, pick a subrange of its input to limit the range that the outer mutators work on, a desirable feature. And all these mutators can change data in-place, which is nicely efficient.

What this syntax cannot do, however, is to modify the size of the container (at least not with vector), so mutators that insert or remove elements can't work with an iterator-based interface. An alternative interface passes the entire container to the mutator. I'm not sure how to deal with subranging in that case, and it also feels like a less elegant solution than the generic and minimal-requirements iterator-based solution.

I would appreciate any ideas on how to deal with this limitation.

A: 

You can combine functors (Think: Function Composition) with boost::bind.

On the other hand I feel I don't fully understand: is the second functor supposed to work on a different range than the first functor? Is data going to be removed from the container?

Be aware: You current solution has non obvious costs: every composition of "mutators" would require a new iteration instead of being "real composition of mutators".

pmr
My example composition is supposed to support a case where a general mutation is limited to a subrange of inputs. For example, one "mutator" can take a sorted sequence of integers and return only the non-negative subsequence, while a second mutator then takes the square root of all the elements in this subrange.As for the efficiency of multiple iterations, I'm not too concerned--mutators are designed to take advantage of random-access containers/iterators.
Eitan
@Eitan In that case "Mutator" is a complete misnomer. You are not mutating the input in anyway, you are selecting pieces of it and in effect you throw iteration and the action that is to be performed on elements together. Maybe iterator adaptors is more what you want.
pmr
Yes, you'd be right about misnaming non-mutating mutators, and the elegant way of mutating a sub-range would be with iterator adapters.But the main problem is still, what is the most elegant interface to mutate when the size of the container might be affected as well.
Eitan