Я начал играть с 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; });
};
Самая сложная проблема — выяснить тип возвращаемого значения в коде. 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";
}
Думаю будет работать
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)>
может быть разумным (будут небольшие накладные расходы во время выполнения).
Надеюсь, что это работает.