08 апреля 2009

Range concatenation

В библиотеках для работы с итераторами (Boost.Iterators, Boost.Range, RangeEx) куча полезных вещей, но почему-то нигде не нашел механизма "склеить" два диапазона в один. Такого, чтобы можно было сделать вот так:
template <class Range> void process_range(Range const &);

std::vector<foo> v = ...;
std::list<foo> l = ...;
process_range(concat(v, l));
Вот разбить диапазон на части - пожалуйста, перемещать значения в случайном порядке - да не вопрос, а вот склеить - нифига.

Можно, конечно, создать вектор из ссылок на foo, заполнить его ссылками на обекты из первого и второго диапазона, но как-то не спортивно: во-первых само выделение динамической памяти для вектора - не слишком быстрая операция, плюс потом еще ссылки туда копировать. Пришлось написать свой велосипед.

С использованием boost::variant все получилось достаточно компактно, хотя не максимально эффективно:
template <class TIterator1, class TIterator2, ...>
class ConcatIterator : public boost::iterator_facade<...>
{
public:
reference dereference() const
{
return boost::apply_visitor(DereferenceVisitor<reference>(), It);
}

void increment()
{
boost::apply_visitor(IncrementVisitor(), It);
StabilizeForward();
}

void decrement()
{
StabilizeBackward();
boost::apply_visitor(DecrementVisitor(), It);
}

bool equal(const ConcatIterator& lhs) const
{
return (Range == lhs.Range) && (It == lhs.It);
}

private:
size_t Range; // 0 for the first range, 1 for the second range
boost::variant<TIterator1, TIterator2> It, End1, Begin2;
};

PS. Нет худа без добра. Мой велосипед вдохновил разработчиков Boost.Range добавить объединение диапазонов в RangeEx.

Комментариев нет: