Переопределить макрос BOOST_FOREACH безопасно
У меня есть следующий макрос:
#define FOREACH(decl, c) BOOST_FOREACH(decl, std::make_pair((c).begin(), (c).end()))
(Я использую этот макрос, потому что мои контейнеры не реализуют изменяемый API итерации.)
Проблема в том, что c
оценивается дважды.
Мой вопрос: можно ли исправить этот макрос так, чтобы:
c
оценивается не более одного раза- Любые локальные переменные, созданные для удовлетворения первого условия, живут только в соответствующей области 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 для получения итераторов. Который есть два способа расширить это:
- Предоставьте функции-члены и вложенный тип: http://www.boost.org/doc/libs/1_48_0/libs/range/doc/html/range/reference/extending/method_1.html
- Предоставляйте автономные функции и специализируйте метафункции: 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
является.
"Выражения-выражения" являются расширением 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++.)