Opened 15 years ago

Closed 15 years ago

Last modified 15 years ago

#1295 closed Feature Requests (wontfix)

for each in VC8+

Reported by: NN Owned by: Eric Niebler
Milestone: To Be Determined Component: foreach
Version: Boost 1.34.1 Severity: Optimization
Keywords: Cc:

Description

VC8 and VC9 have built in for each operator. BOOST_FOREACH should use it instead. This can decrease compilation time.

Code sample:

#if defined(_MSC_VER) && _MSC_VER >= 1400
 #define BOOST_FOREACH(i, c) for each(i in c)
#else
 // BOOST_FOREACH
#endif // _MSC_VER && _MSC_VER >= 1400

Change History (7)

comment:1 by Eric Niebler, 15 years ago

Resolution: wontfix
Status: newclosed

Can't be done. MSVC 'for each' can't operate on native array types, iterator ranges or C strings nor is extensible via Boost.Range customization points. 'for each' also cannot iterate over an STL container using a non-const reference iteration variable.

Nobody has complained about compile times with BOOST_FOREACH.

comment:2 by NN, 15 years ago

VC can iterate over native array and iterator ranges. In VC9 you can iterate over string literals too.

There is no option to change variables, but this can be easily fixed with const_cast.

This what you can do with VC9 for each:

#include <boost/array.hpp>
#include <boost/range/iterator_range.hpp>

#include <vector>

template<typename T, size_t N>
boost::array<T, N> make_array_range()
{
    boost::array<T, N> a;
    T c = 0;
    for each(T const& i in a)
        const_cast<T&>(i) = c++;
    return a;
}

int main()
{
    for each(int i in make_array_range<int, 10>())
        printf("%d", i);

    typedef boost::array<int, 10> ints;
    ints a = make_array_range<int, 10>();
    boost::iterator_range<ints::iterator> x(a.begin(), a.end());
    for each(ints::const_reference i in x)
        printf("%d", i);

    for each(char% c in "asdf")
        printf("%c", c);

    typedef std::vector<int> vints;
    vints v(a.begin(), a.end());
    for each(vints::const_reference i in v)
        const_cast<vints::reference>(i) = 0;
}

Of course this is not full replacement of Boost.Foreach.

comment:3 by NN, 15 years ago

Resolution: wontfix
Status: closedreopened

I have found the way to make it work with all requirements:

#include <boost/range/iterator_range.hpp>
#include <boost/foreach.hpp>

#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC 8.0+   
    #define foreach(var, col) \
        for each(var in (::boost::make_iterator_range(col)))
#else // Other compilers
    #define foreach BOOST_FOREACH
#endif  // defined(_MSC_VER) && _MSC_VER >= 1400 // VC 8.0+

There is a code for test:

#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/array.hpp>
#include <boost/lambda/lambda.hpp>
#include <vector>
#include <cassert>
#include <iostream>
 
#include <iterator>

namespace my
{

// Extensibility check
struct non_iteratable
{
    non_iteratable()
    {
        x[0] = 0;
        x[1] = 1;
    }

    typedef boost::array<int, 2> x_type;
    x_type x;
};

non_iteratable::x_type::iterator boost_range_begin(non_iteratable& c)
{
    return boost::begin(c.x);
}

non_iteratable::x_type::iterator boost_range_end(non_iteratable& c)
{
    return boost::end(c.x);
}

non_iteratable::x_type::const_iterator boost_range_begin(non_iteratable const& c)
{
    return boost::begin(c.x);
}

non_iteratable::x_type::const_iterator boost_range_end(non_iteratable const& c)
{
    return boost::end(c.x);
}

} // namespace my

namespace boost
{

// specialize rannge_iterator and range_const_iterator in namespace boost
template<>
struct range_iterator<my::non_iteratable>
{
    typedef my::non_iteratable::x_type::iterator type;
};

template<>
struct range_const_iterator<my::non_iteratable>
{
    typedef my::non_iteratable::x_type::const_iterator type;
};

} // namespace boost


int main()
{
    {
    // Regular array
    int a[10] = {};
    // Value
    foreach(int i, a)
        std::cout << i;

    // Reference to const
    foreach(int const& i, a)
        std::cout << i;

    // Reference to non Const
    foreach(int& i, a)
        i = 1;
    assert(std::find(boost::begin(a), boost::end(a), 0) == boost::end(a));
    }

    {
    // Boost.Array
    boost::array<int, 10> a = {};
    // Value
    foreach(int i, a)
        std::cout << i;

    // Reference to const
    foreach(int const& i, a)
        std::cout << i;

    // Reference to non Const
    foreach(int& i, a)
        i = 1;
    assert(std::find(boost::begin(a), boost::end(a), 0) == boost::end(a));
    }

    {
    // Std.Vector
    std::vector<int> a(10);
    // Value
    foreach(int i, a)
        std::cout << i;

    // Reference to const
    foreach(int const& i, a)
        std::cout << i;

    // Reference to non Const
    foreach(int& i, a)
        i = 1;
    assert(std::find(boost::begin(a), boost::end(a), 0) == boost::end(a));
    }

    // Extensibility
    {
    my::non_iteratable a;
    
    // Value
    foreach(int i, a)
        std::cout << i;

    // Reference to const
    foreach(int const& i, a)
        std::cout << i;

    // Reference to non Const
    foreach(int& i, a)
        i = 1;
    assert(std::find(boost::begin(a), boost::end(a), 0) == boost::end(a));
    }
}

The code works as expected. Thank you for attention.

comment:4 by Eric Niebler, 15 years ago

Resolution: wontfix
Status: reopenedclosed

Sorry, no. If you try replacing the BOOST_FOREACH macro as you suggest and run the foreach regression tests, you'll see that this does not handle rvalue collections correctly. Besides, BOOST_FOREACH has well-defined customization hooks, which this implementation is not respecting. There is simply no way.

comment:5 by NN, 15 years ago

Thanx, I will check the regression tests.

comment:6 by NN, 15 years ago

Here: http://rsdn.ru/forum/message/2907464.1.aspx I have some for each implementation. It does not passes rvalue.

But the code is more optimized than BOOST_FOREACH.

comment:7 by anonymous, 15 years ago

I'll look at it once you get it to pass all of the regression tests.

Note: See TracTickets for help on using tickets.