Сделайте мой класс С++ итерабельным через 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-го элемента.

Итак, в основном я хочу, чтобы мой класс вел себя так, как будто у него была коллекция, но на самом деле у нее ее нет. Любые идеи?

Ответ 1

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

Библиотека Boost.Iterator содержит несколько полезных шаблонов. Я использовал свой базовый класс Iterator Facade пару раз, и это приятно и легко определить ваши собственные итераторы, используя его.

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

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

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

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

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

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

Ответ 2

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

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

Ответ 3

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

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