【问题标题】:Sequential heterogeneous functions executer顺序异构函数执行器
【发布时间】:2019-06-04 08:19:07
【问题描述】:

假设有 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); 

给定第一个函数的输入(在 f 的情况下为 int),我希望这些函数按顺序执行,每个函数都将前一个函数的输出作为输入。

您认为在 C++ 中可能吗?可能不使用 dynamic_cast 和使用 C++11

【问题讨论】:

  • 在运行时将所有函数放在一个容器中会很困难,因为它们都有不同的类型。
  • Container 是否需要在运行时修改?如果我们在编译时添加函数可以吗?
  • Container 可以有三个字段来存储三个不同的函数指针,add 可以是设置各自函数指针的三个重载设置器,最后Container 有一个函数来执行f(g(h())) 来电。
  • @NeilButterworth:我将句子解析为“没有dynamic_cast,使用C++11”
  • @svoltron 至于编译时解决方案(不需要std::function 开销),请查看这个:stackoverflow.com/questions/54076787/…。问题是如何获得最终类型,但解决方案只是构建调用表达式,然后执行decltype,所以跳过后者。

标签: c++ c++11 inheritance types template-meta-programming


【解决方案1】:

如果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

live example on 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);
}

【讨论】:

  • 非常有趣的解决方案 Vittorio
  • 唯一值得怀疑的是 std::function 的性能:/ TODO 部分也是我想知道现在该怎么做的事情 eheh
  • @svoltron:你需要某种类型的擦除。联系std::function 很容易,而不是自己动手。
  • @svoltron:添加了 TODO 部分的可能实现。它似乎有效,但尚未测试所有边缘情况。
  • @VittorioRomeo:here is another recursive impl,也许你可以做得更好。
【解决方案2】:

这更像是一个旁注,但我在假期期间做了一些非常相似的东西来娱乐。这里的代码太多,但您可以查看代码 here 或实时版本 here。

基本上它是一个函数,它返回一个捕获所有要调用的函数的 lambda。这允许您编写以下代码:

#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::invoke 不起作用,它还将尝试使用 std::apply 扩展返回的元组。这使得以下代码有效:

#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
}

正如我所说,这只是一个有趣的实验,但也许会有所帮助!

【讨论】:

    猜你喜欢
    • 2015-08-02
    • 1970-01-01
    • 2010-12-25
    • 2020-07-08
    • 2017-06-13
    • 2016-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多