Есть ли проблемы с этой реализацией макроса foreach?

В расширении Intel Cilk Plus есть это ключевое слово cilk_for (или, собственно, _Cilk_for). Это как ключевое слово для, но более ограниченное и его итерации выполняются параллельно. Я написал вспомогательный макрос в духе BOOST_FOREACH, который использует cilk_for для внутреннего использования. Вы видите какие-либо проблемы со следующей реализацией?

#pragma once

#include <iterator>
#include <boost/preprocessor/cat.hpp>
#include <cilk/cilk.h>

#define cilk_foreach(_decl_var_, _expr_range_) \
    CILK_FOREACH_I(_decl_var_, _expr_range_, __COUNTER__)

#define CILK_FOREACH_I(_decl_var_, _expr_range_, _id_)               \
    CILK_FOREACH_II(_decl_var_,                                      \
                    _expr_range_,                                    \
                    BOOST_PP_CAT(_range_3s236dw221GyVcf46_, _id_),   \
                    BOOST_PP_CAT(_end_5Y60u42bIp7DZd88f2c_, _id_),   \
                    BOOST_PP_CAT(_itr_6V970q8n4Etv0i8bf50_, _id_),   \
                    BOOST_PP_CAT(_continue_4rtWH641r5cXqU_, _id_))

#define CILK_FOREACH_II(_decl_var_, _expr_range_, _range_, _end_, _itr_, _cont_) \
    auto&& _range_ = _expr_range_;                                               \
    auto   _end_   = std::end(_range_);                                          \
                                                                                 \
    cilk_for (auto _itr_ = std::begin(_range_); _itr_ != _end_; ++_itr_)         \
        if (auto _cont_ = true)                                                  \
            for (_decl_var_ = *_itr_; _cont_; _cont_ = false)

Вы бы использовали это так:

std::vector<int> values (10);

cilk_foreach (auto& value , values)
{
    value += 123;
}

РЕДАКТИРОВАТЬ

template <typename T>
struct Wrap
{
    T& data;

    explicit Wrap(T&& data)
        : data (data)
    {}

    operator bool() const
    {
        return true;
    }
};

template <typename T>
Wrap<T> wrap(T&& data)
{
    return Wrap<T>(std::forward<T>(data));
}

#define cilk_foreach(_decl_var_, _expr_range_) \
    CILK_FOREACH_I(_decl_var_, _expr_range_, __COUNTER__)

#define CILK_FOREACH_I(_decl_var_, _expr_range_, _id_)              \
                                                                    \
    CILK_FOREACH_II(_decl_var_,                                     \
                    _expr_range_,                                   \
                    BOOST_PP_CAT(_range_3s236dw221GyVcf46_, _id_),  \
                    BOOST_PP_CAT(_itr_6V970q8n4Etv0i8bf50_, _id_),  \
                    BOOST_PP_CAT(_continue_4rtWH641r5cXqU_, _id_))

#define CILK_FOREACH_II(_decl_var_, _expr_range_, _range_, _itr_, _cont_)  \
                                                                           \
    if (auto _range_ = wrap(_expr_range_))                                 \
        cilk_for (auto _itr_  = std::begin(_range_.data);                  \
                       _itr_ != std::end  (_range_.data);                  \
                     ++_itr_)                                              \
            if (auto _cont_ = true)                                        \
                for (_decl_var_ = *_itr_; _cont_; _cont_ = false)

2 ответа

Решение

Вы, вероятно, должны прочитать это: C++ Boost: Есть ли ошибки с BOOST_FOREACH?

Одна очевидная проблема заключается в том, что ваш марко не расширяется до чего-то такого лексического типа, как это выглядит. В качестве примера рассмотрим

if(condition)
    cilk_foreach (auto& value , values) {
        //stuff
    }

или даже хуже

if(condition)
    cilk_foreach (auto& value , values) 
        // one-line stuff
else
    // other stuff

Ответ Хуркила правильный, чтобы избежать этой ошибки, я использую этот макрос:

#define foreach(ARRAY, ITEM) \
    for (size_t i = 0, b = 1; b && i != (ARRAY).size(); ++i, b = !b) \
        for (auto &ITEM = (ARRAY)[i]; b; b = 0)

Да, у него есть некоторые недостатки, но он демонстрирует реальное поведение цикла for, даже когда он вложен, как в другом ответе. Перерыв и продолжить работу, как ожидалось.

И вы бы использовали это так:

std::vector<int> values(10);
foreach (values, val)
    val += 123;
Другие вопросы по тегам