views:

102

answers:

3

I wanted to create a little macro to simulate for(auto item : container) in VC2010 which I can then replace with the real construct when it's released.

There is BOOST_FOREACH, however I would like auto support.

I've tried creating a macro. However it fails when the dereferenced iterator is a constant type.

#define _LIB_FOREACH_LINENAME_CAT(name, line) name##line
#define _LIB_FOREACH_LINENAME(name, line) _LIB_FOREACH_LINENAME_CAT(name, line)

#define LIB_AUTO_FOREACH(item, expr) \
    decltype((expr))&& _LIB_FOREACH_LINENAME(con, __LINE__)((expr)); auto _LIB_FOREACH_LINENAME(it, __LINE__) = _LIB_FOREACH_LINENAME(con, __LINE__).begin(); for(auto (item) = *_LIB_FOREACH_LINENAME(con, __LINE__).begin(); _LIB_FOREACH_LINENAME(it, __LINE__) != _LIB_FOREACH_LINENAME(con, __LINE__).end(); ++_LIB_FOREACH_LINENAME(it, __LINE__), (item) = *_LIB_FOREACH_LINENAME(it, __LINE__))

Anyone up for the challenge to correct mine or find a working implementation?

EDIT:

Notice that (expr) should only be evaluated once.

+2  A: 

Instead of

decltype((expr))&&

you should probably just write

auto&&

A problem with your approach is that it doesn't work with ranges that don't provide begin/end member functions (such as arrays). The proposed C++0x for-range loop makes use of free begin/end functions and argument dependent lookup to solve this.

Perhaps it's a good idea to include the code block as macro parameter:

#include <cstddef>

namespace for_range_helpers {

template<class Container>
auto begin(Container& c) -> decltype(c.begin())
{ return c.begin(); }

template<class Container>
auto end(Container& c) -> decltype(c.end())
{ return c.end(); }

template<class T, std::size_t N>
T* begin(T (&array)[N])
{ return array+0; }

template<class T, std::size_t N>
T* end(T (&array)[N])
{ return array+N; }

} // namespace for_range_helpers

#define FOR_RANGE(init_,expression_,...) \
    do { \
        using ::for_range_helpers::begin; \
        using ::for_range_helpers::end; \
        auto&& range_ = expression_; \
        for (auto iter_ = begin(range_), end_ = end(range_); \
            iter_ != end_; ++iter_) \
        { \
            init_ = *iter_; \
            __VA_ARGS__ \
        } \
    } while (0)

#include <iostream>
#include <vector>

int main()
{
    int arr[] = {2,3,5,7,11};
    std::vector<int> vec;
    FOR_RANGE(auto i, arr,
        vec.push_back(i);
    );
    FOR_RANGE(auto i, vec,
        std::cout << ' ' << i;
    );
    std::cout << '\n';
}
sellibitze
Instead of using `++iter_` use `std::advance(iter_,1)` this allows people with odd code write their own
KitsuneYMG
It's a terrible idea to include the code block as a macro parameter. You will run into comma issues.
caspin
@caspin: I would have agreed but C++0x's preprocessor uses supports `__VA_ARGS__`. Do you still think there are any comma issues left?
sellibitze
Good point. It took me a sec to figure out why `__VA_ARGS__` would fix the commas issue. For those as slow as me, the variable macro params makes the preprocessor not care how the commas are parsed because all the `__VA_ARGS__` params are dumped in the for loop body any ways. If the macro ends up parsing you for loop body as 42 different parameters because of commas, you'll never know as all 42 get pasted correctly. I still prefer `BOOST_FOREACH` as the body sits below the macro as you would normally see with a regular `for` loop. I don't know how they do it but I like the affect.
caspin
+3  A: 

There is BOOST_FOREACH, however I would like auto support.

BOOST_FOREACH appears to support the C++0x auto keyword alright.

As your macro is, boost's one is superior. It also works for arrays (and probably zero-terminated char arrays) and lets you use references for the loop variable, rather than making it appear from the thin air.

UncleBens
A: 

In VS2010 you could also use Microsofts for each (auto i in v) extension. Personally though, I would prefer Boosts cross-platform solution.

See also here.

Maybe it is possible to write a macro that converts the C++0x-syntax to Microsofts syntax.

George