Переопределите макрос BOOST_FOREACH безопасно - PullRequest
2 голосов
/ 14 января 2012

У меня есть следующий макрос:

#define FOREACH(decl, c) BOOST_FOREACH(decl, std::make_pair((c).begin(), (c).end()))

(я использую этот макрос, потому что мои контейнеры не поддерживают API изменяемой итерации.)

Проблема в том, что c оценивается дважды.

Мой вопрос: можно ли исправить этот макрос так:

  1. c оценивается не более одного раза
  2. Любые локальные переменные, созданные для удовлетворения первого условия, живут только в пределах соответствующей области foreach.

Ответы [ 3 ]

8 голосов
/ 14 января 2012

Вы можете использовать встроенную вспомогательную функцию.

#define FOREACH(decl, c) BOOST_FOREACH(decl, pair_helper(c))

template <typename T>
inline std::pair<typename T::iterator, typename T::iterator> pair_helper (T c) {
    return std::make_pair(c.begin(), c.end());
}
6 голосов
/ 14 января 2012

Нет необходимости в этой хакерской атаке.Boost.Foreach использует Boost.Range для получения итераторов.Существует два способа его расширения:

  1. Предоставление функций-членов и вложенного типа: http://www.boost.org/doc/libs/1_48_0/libs/range/doc/html/range/reference/extending/method_1.html
  2. Предоставление автономных функций и специализированных метафункций: http://www.boost.org/doc/libs/1_48_0/libs/range/doc/html/range/reference/extending/method_2.html

Теперь в вашем случае похоже, что вы предоставляете функции-члены begin() и end(), но не предоставляете вложенный тип iterator (я полагаю, это то, что вы подразумеваете под изменяемым API итерации).Вы можете сделать одну из двух вещей.

Во-первых, вы можете typedef тип вложенного итератора следующим образом:

typedef const_iterator iterator;

Во-вторых, если вы не можете изменить класс, вы можете специализировать метафункции, например так (заменяяYourContainer с любым типом вашего контейнера):

namespace boost
{
    //
    // Specialize metafunctions. We must include the range.hpp header.
    // We must open the 'boost' namespace.
    //

    template< >
    struct range_mutable_iterator< YourContainer >
    {
        typedef YourContainer::const_iterator type;
    };

    template< >
    struct range_const_iterator< YourContainer >
    {
        typedef YourContainer::const_iterator type;
    };

} // namespace 'boost'

Конечно, я предполагаю, что у вас есть const_iterator typedef в вашем классе (так как вы сказали, что он не поддерживает изменяемость).Если вы этого не сделаете, вам нужно будет заменить YourContainer::const_iterator на любой тип, которым является const_iterator.

2 голосов
/ 14 января 2012

«Выражения-выражения» являются расширением gcc / g ++ , чтобы избежать повторной оценки макропараметров

#define make_pair_of_iterators(c) ({typeof(c)& c_ = (c); make_pair(c_.begin(), c_.end()); })

Тогда вы можете сделать:

#define FOREACH(decl, c) BOOST_FOREACH(decl, make_pair_of_iterators(c) )

typeof также является расширением gcc / g ++.)

...