Redefine BOOST_FOREACH macro safely

474 views Asked by At

I have the following macro:

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

(I am using this macro, because my containers do not implement the mutable iteration API.)

The problem with it, is that c is evaluated twice.

My question is can this macro be fixed so that:

  1. c is evaluated at most once
  2. Any local variables created in order to satisfy the first condition live within the respective foreach scope only.
3

There are 3 answers

4
Alexander Kondratskiy On BEST ANSWER

You could use an inline helper function.

#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());
}
0
Paul Fultz II On

There is no need for this hackery. Boost.Foreach relies on Boost.Range in order to retrieve the iterators. Which there are two ways to extend that:

  1. Provide member functions and a nested type: http://www.boost.org/doc/libs/1_48_0/libs/range/doc/html/range/reference/extending/method_1.html
  2. Provide free-standing functions and specialize metafunctions: http://www.boost.org/doc/libs/1_48_0/libs/range/doc/html/range/reference/extending/method_2.html

Now in your case, it looks like you provide the begin() and end() member functions, but don't provide the nested type iterator(I assume thats what you mean by the mutable iteration API). You can do one of two things.

First, you can typedef the nested iterator type like this:

typedef const_iterator iterator;

Secondly, if you can't modify the class, you can specialize the metafunctions, like this(replacing YourContainer with whatever your container type is):

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'

Of course I assume you have a const_iterator typedef'd in your class(Since you said it doesn't support mutable). If you don't, you will need to replace YourContainer::const_iterator with whatever type your const_iterator is.

0
Aaron McDaid On

'Statement-expressions' are a gcc/g++ extension to avoid repeated evaluation of macro arguments

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

Then you could do:

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

(And typeof is a gcc/g++ extension also.)