Переопределить макрос BOOST_FOREACH безопасно

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

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

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

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

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

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

3 ответа

Решение

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

#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());
}

Там нет необходимости для этой взлома. 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_iteratortypedefбыл в вашем классе (так как вы сказали, что он не поддерживает изменяемость). Если вы этого не сделаете, вам нужно будет заменить YourContainer::const_iterator с любым типом вашего const_iterator является.

"Выражения-выражения" являются расширением 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++.)

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