Составные адаптеры в Boost :: range

Я начал играть с Boost :: Range, чтобы иметь конвейер ленивых преобразований в C ++. Моя проблема сейчас заключается в том, как разделить трубопровод на более мелкие части. Предположим, у меня есть:

int main(){
auto map = boost::adaptors::transformed; // shorten the name
auto sink = generate(1) | map([](int x){ return 2*x; })
| map([](int x){ return x+1; })
| map([](int x){ return 3*x; });
for(auto i : sink)
std::cout << i << "\n";
}

И я хочу заменить первые две карты magic_transformт.е.

int main(){
auto map = boost::adaptors::transformed; // shorten the name
auto sink = generate(1) | magic_transform()
| map([](int x){ return 3*x; });
for(auto i : sink)
std::cout << i << "\n";
}

Как бы написать magic_transform? Я посмотрел вверх Boost :: Документация по ассортименту, но я не могу понять это.

добавлениеЯ хочу написать такой класс:

class magic_transform {
... run_pipeline(... input) {
return input | map([](int x){ return 2*x; })
| map([](int x){ return x+1; });
};

10

Решение

Самая сложная проблема — выяснить тип возвращаемого значения в коде. decltype и лямбды плохо смешиваются (посмотреть здесь), поэтому мы должны думать об альтернативном пути:

auto map = boost::adaptors::transformed;

namespace magic_transform
{
std::function<int(int)> f1 = [](int x){ return 2*x; };
std::function<int(int)> f2 = [](int x){ return x+1; };
template <typename Range>
auto run_pipeline(Range input) -> decltype(input | map(f1) | map(f1))
{
return input | map(f1) | map(f2);
}
}

...
auto sink = magic_transform::run_pipeline(generate(1))
| map([](int x){ return 3*x; });

Простое решение — воткнуть лямбды в std::function так что мы можем использовать decltype вывести тип возврата. Я использовал пространство имен magic_transform в примере, но вы могли бы адаптировать этот код в класс, если вы хотели бы также. Вот ссылка адаптируя ваш код к вышесказанному.

Кроме того, используя std::function может быть излишним здесь. Вместо этого вы можете просто объявить две нормальные функции (пример).

Я также экспериментировал с boost::any_rangeКажется, есть некоторые несовместимости с лямбдами C + 11 и т. д. Самое близкое, что я мог получить, было следующее (пример):

auto map = boost::adaptors::transformed;
using range = boost::any_range<
const int,
boost::forward_traversal_tag,
const int&,
std::ptrdiff_t
>;

namespace magic_transform
{
template <typename Range>
range run_pipeline(Range r)
{
return r | map(std::function<int(int)>([](int x){ return 2*x; }))
| map(std::function<int(int)>([](int x){ return x+1; }));
}
}

int main(){
auto sink = magic_transform::run_pipeline(boost::irange(0, 10))
| map([](int x){ return 3*x; });
for(auto i : sink)
std::cout << i << "\n";
}
4

Другие решения

Думаю будет работать

auto magic_transform()->decltype(boost::adaptors::transformed(std::function<int(int)>())
{
std::function<int(int)> retval = [](int x){ return [](int x){ return x+1; }(2*x);
return boost::adaptors::transformed(retval);
}

но это, вероятно, не то, что вы ищете. 🙂 (Шутки в вышеприведенном коде: лямбда-цепочки для 2 * x + 1, использование decltype в основном для реализации, чтобы найти тип возвращаемого значения),

Глядя на исходный код для http://www.boost.org/doc/libs/1_46_1/boost/range/adaptor/transformed.hppтип, который magic_transform хочет вернуться boost::range_detail::transform_holder<T>где T — тип функции.

Когда вы делаете это в стеке с лямбдами, T заканчивает тем, что был очень узким типом. Если вы хотите обойти абстрактные преобразования, не раскрывая все детали, используя std::function<outtype(intype)> может быть разумным (будут небольшие накладные расходы во время выполнения).

Надеюсь, что это работает.

1

По вопросам рекламы [email protected]