пятница, 11 сентября 2009 г.

Extending BOOST_FOREACH

Just for fun занимаюсь дома проектом на C++... Встала задача предоставить пользователю возможность пробежаться по элементам внутреннего STL контейнера, без возможности издеваться над самим контейнером. Вытаскивать и begin и end ради такого довольно неудобно. Видел возможный вариант решения проблемы в графическом движке Ogre(немного укоротил, оставил суть):
 template <class T>
class VectorIterator
{
private:
typename T::iterator mCurrent;
typename T::iterator mEnd;
VectorIterator() {};
public:
typedef typename T::value_type ValueType;

VectorIterator(typename T::iterator start, typename T::iterator end)
: mCurrent(start), mEnd(end)
{
}

explicit VectorIterator(T& c)
: mCurrent(c.begin()), mEnd(c.end())
{
}

bool hasMoreElements(void) const
{
return mCurrent != mEnd;
}

typename T::value_type getNext(void)
{
return *mCurrent++;
}
};


Все прекрасно, за исключением того что по такому чуду инженерной мысли не естественно не будет работать BOOST_FOREACH, что явно не удобно и не привычно. Официальные доки Boost для решения подобных вопросов предлагают нам 2 возможных выхода:

  • Предоставить typdef на iterator и const_iterator и вытащить наружу begin и end.
  • Специализировать range_begin,range_end etc

Комбинируя оба варианта и используя friend можно предоставить пользователю доступ к BOOST_FOREACH и в тоже время спрятать от него настоящие итераторы...
template<typename T> class CollectionWrapper
{
public:
typedef typename T::iterator iterator;
typedef typename T::const_iterator const_iterator;

CollectionWrapper(T& collection):begin(collection.begin()),end(collection.end()){}

private:
iterator begin;
iterator end;

CollectionWrapper(){}

friend iterator range_begin( CollectionWrapper<T> & x ){return x.begin;}
friend iterator range_end( CollectionWrapper<T> & x ) {return x.end;}

friend const_iterator range_begin( CollectionWrapper<T> const & x ){return x.begin;}
friend const_iterator range_end( CollectionWrapper<T> const & x ) {return x.end;}
};

И теперь можно пользоваться:
std::string str = "some string";
CollectionWrapper<std::string> s(str);

BOOST_FOREACH(char ch, s)
std::cout<<ch<<std::endl;

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