Сделайте мой класс C++ повторяемым через BOOST_FOREACH

У меня есть класс, который я хочу представить список структур (которые просто содержат некоторые целые числа). Я не хочу, чтобы извне модифицировали эти данные, просто переберите их и прочитайте их Пример:

struct TestData
{
  int x;
  int y;
  // other data as well
}

class IterableTest
{
  public:
    // expose TestData here
};

Теперь в моем коде я хочу использовать свой класс следующим образом:

IterableTest test;
BOOST_FOREACH(const TestData& data, test.data())
{
  // do something with data
}

Я уже читал эту статью http://accu.org/index.php/journals/1527 о членских пространствах. Однако я не хочу (или не могу) сохранить все TestData во внутреннем векторе или что-то в этом роде. Это связано с тем, что сам класс не владеет хранилищем, то есть на самом деле нет базового контейнера, к которому класс может обращаться напрямую. Однако сам класс может запрашивать внешний компонент для получения следующего, предыдущего или i-го элемента.

Поэтому я хочу, чтобы мой класс вел себя так, как если бы у него была коллекция, но на самом деле его нет. Есть идеи?

3 ответа

Решение

Похоже, вы должны написать свои собственные итераторы.

В библиотеке Boost.Iterator есть несколько полезных шаблонов. Я пару раз использовал их базовый класс Iterator Facade, и с его помощью легко и просто определить свои собственные итераторы.

Но даже без этого итераторы не являются ракетостроением. Они просто должны выставить правильные операторы и typedefs. В вашем случае они просто будут обертками вокруг функции запроса, которую они должны вызывать при увеличении.

Как только вы определили класс итератора, вам просто нужно добавить begin() а также end() функции-члены для вашего класса.

Похоже, основная идея заключается в том, чтобы вызвать функцию запроса при увеличении итератора, чтобы получить следующее значение. И разыменование должно затем вернуть значение, полученное из последнего запроса запроса.

Это может помочь взглянуть на стандартную библиотеку stream_iterators для некоторой семантики, так как они также должны работать вокруг некоторого подозрительного "у нас на самом деле нет контейнера, и мы не можем создавать итераторы, указывающие куда-либо, кроме текущей позиции потока".

Например, если вам нужно позвонить query() Функция, которая возвращает NULL, когда вы достигли конца последовательности, создание "конечного итератора" будет непростым делом. Но на самом деле все, что вам нужно, это определить равенство так, чтобы "итераторы были равны, если они оба хранят NULL в качестве своего кэшированного значения". Поэтому инициализируйте "конечный" итератор с помощью NULL.

Это может помочь найти требуемую семантику для итераторов ввода или, если вы читаете документацию для Boost.Iterator, в частности, для однопроходных итераторов. Вы, вероятно, не сможете создавать многопроходные итераторы. Так что посмотрите, какое именно поведение требуется для однопроходного итератора, и придерживайтесь этого.

На странице документации для Boost FOR_EACH:

BOOST_FOREACH перебирает последовательности. Но что именно называется последовательностью? Поскольку BOOST_FOREACH построен поверх Boost.Range, он автоматически поддерживает те типы, которые Boost.Range распознает как последовательности. В частности, BOOST_FOREACH работает с типами, которые соответствуют концепции однопроходного диапазона. Например, мы можем использовать BOOST_FOREACH с:

Если ваш тип коллекции представляет стандартный контейнерный интерфейс, вам не нужно ничего делать, чтобы BOOST_FOREACH работать с вашим типом. Другими словами, если ваш тип имеет iterator а также const_iterator вложенные определения типов и begin() а также end() функции-члены, BOOST_FOREACH уже знает, как перебрать ваш тип. Никаких дальнейших действий не требуется.

http://boost-sandbox.sourceforge.net/libs/foreach/doc/html/boost_foreach/extending_boost_foreach.html

Другие вопросы по тегам