2009-10-20 2 views
11

У меня есть класс, который я хочу выставить для списка структур (которые содержат только целые числа). Я не хочу снаружи, чтобы изменить эти данные, просто перебрать его и читать их Пример:Сделайте мой класс 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-го элемента.

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

+4

Вам не нужно предоставлять функции начала/конца, возвращающие подходящие итераторы? – jalf

+0

да, но у меня нет базового контейнера, который мог бы предоставить мне эти итераторы – newgre

+0

, поэтому напишите их сами. :) Библиотека Boost.Iterator должна быстро запускать вас и запускать. – jalf

ответ

5

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

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

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

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

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

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

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

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

+0

И потому, что я хороший парень: концепция ввода итератора >> http://www.sgi.com/tech/stl/InputIterator.html –

0

Если ваш тип коллекции представляет стандартный контейнерный интерфейс, вам не нужно ничего делать, чтобы сделать 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

+0

Я знаю об этом, но откуда я могу получить эти итераторы? Нет базового контейнера, итераторы которого я мог бы использовать – newgre

0

На странице for_each документации Форсирования:

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

Смежные вопросы