Исполнитель последовательных гетерогенных функций - PullRequest
0 голосов
/ 09 января 2019

давайте предположим, что есть N функций, которые принимают разные входные данные и возвращают разные значения:

int h(int);
string g(int);
double f(string);

Конечно, можно написать что-то вроде:

f(g(h(4)))  

Предположим теперь, что я хочу сохранить эти функции f, g, h в виде контейнера вроде:

Container c;
c.add(h);
c.add(g);
c.add(f); 

Учитывая ввод для первой функции (int в случае f), я бы хотел, чтобы эти функции выполнялись последовательно, причем каждая из них принимала в качестве входных данных вывод предыдущей.

Как вы думаете, возможно ли это в C ++? возможно без использования dynamic_cast и C ++ 11

Ответы [ 2 ]

0 голосов
/ 09 января 2019

Это скорее дополнительная заметка, но я сделал что-то очень похожее для развлечения во время курортного сезона Здесь слишком много кода, чтобы его вместить, но вы можете проверить код здесь или живую версию здесь .

в основном это функция, которая возвращает лямбду, фиксирующую все функции для вызова. Это позволяет вам написать следующий код:

#include <iostream>
#include <string>

#include "chain.hpp"

int f(int x) { return x; }
std::string g(int x) { return std::to_string(x); }
double h(std::string x) { return x.size(); }

int main() {
    auto function = fn::chain(f, g, h);
    std::cout << function(5) << '\n'; // prints 1

    return 0;
}

Он также попытается расширить восстановленные кортежи с помощью std :: apply, если std :: invoke не работает. Это делает следующий код действительным:

#include <iostream>
#include <tuple>

#include "chain.hpp"

int main() {
    auto test = fn::chain([](int i, int j) { return i + j; },
                          [](int i) { return i * 2; },
                          [](int i) -> std::tuple<int, int> {
                              return {i, i};
                          },
                          [](int i, int j) { return i + j; });

    std::cout << test(1, 2) << '\n'; // prints 12
}

Как я уже сказал, это был просто забавный опыт, но, может быть, это будет полезно!

0 голосов
/ 09 января 2019

Если Container необходимо изменить во время выполнения, вы можете добиться этого с помощью std::any (или boost::any):

struct Container
{   
    std::vector<std::function<std::any(std::any)>> _fns;

    template <typename R, typename A>
    void add(R(*f)(A)) 
    { 
        _fns.emplace_back([f](std::any x) -> std::any
        {
            return {f(std::any_cast<A>(x))};
        }); 
    }

    template <typename T>
    std::any call(T x)
    {
        // TODO: replace with recursive version
        return _fns[2](_fns[1](_fns[0](x)));
    }
};

Использование:

int h(int x) { return x; }
std::string g(int x) { return std::to_string(x); }
double f(std::string x) { return x.size(); }

int main()
{
    Container c;
    c.add(h);
    c.add(g);
    c.add(f); 

    std::cout << std::any_cast<double>(c.call(5)) << '\n';
}

Выход:

1

живой пример на wandbox.org


Возможна рекурсивная реализация:

template <typename T>
std::any call_impl(T x, const std::size_t next)
{
    return next == _fns.size() - 1 
        ? _fns[next](x) 
        : call_impl(_fns[next](x), next + 1);
}

template <typename T>
std::any call(T x)
{
    return call_impl(x, 0);
}
...