传递n个参数的函数作为n-x参数的函数

时间:2016-03-06 20:17:02

标签: c++ function c++11 currying reduction

这是一个非常基本的问题,我确信之前已经回答过,但我不知道该搜索什么。

陈述我有一个集成数学函数的函数:

double integrator(double (*func_to_integrate)(double,double));

但是我的集成功能是允许我操作两个以上参数的类型,例如:

double func_to_integrate(double mu, double w0, double x, double y);

这样我就可以遍历mu和w0的不同值并比较整合的结果。 如何将func_to_integrate等函数传递给积分器?

问候

编辑:正如阿兰在评论中指出的那样,部分重复:How can currying be done in C++?

是否有一个优雅的解决方案在函数指针上执行currying操作?

4 个答案:

答案 0 :(得分:2)

鉴于您可以更改integrator功能的签名,有几种解决方案。基本的两个方向是

  1. 使用通用模板参数而不是函数指针( - 调用者必须知道要传递的正确签名),或者
  2. 使用std::function<double(double, double)>作为函数参数。
  3. 两种选择都允许您传递一般函数对象(仿函数,lambdas,std::bind - 对象等)。我选择替代方案1.因为它通常会提供更好的表现。

    然后你可以轻松设置一个lambda:

    double mu = 1.0;
    double w0 = 1.0;
    auto f = [mu, w0] (double x, double y) { return func_to_integrate(mu, w0, x, y); };
    

    并将f传递给您(已加密的)integrator例行程序。

    如果您无法更改功能签名,则可以选择其他方式 - 因为第三方库通常就是这种情况。

    我首先想到的是在这种情况下没有解决方案,因为你不能将一般的仿函数绑定到函数指针。但后来我在this answer中遇到了一个好主意(我稍微调整了一下):用静态std::function变量编码所有内容,然后用静态函数调用这个std::function对象。由于静态函数只是全局函数的语法糖,因此可以设置一个函数指针:

    template <typename Res, typename... Args>
    struct function_ptr_helper
    {
    public:
        template<typename function_type>
        static auto bind(function_type&& f) { func = std::forward<function_type>(f); }
    
        static auto invoke(Args... args) { return func(args...); }
        static auto* ptr() { return &invoke; }
    
    private:
        static std::function<Res(Args ...)> func;
    };
    
    template <typename Res, typename... Args>
    std::function<Res(Args ...)> function_ptr_helper<Res, Args...>::func;
    
    template <typename Res, typename ... Args>
    auto* get_function_ptr(std::function<Res(Args...)> f)
    {
        using type = function_ptr_helper<Res, Args...>;
    
        type::bind(std::move(f));
        return type::ptr();
    }
    

    DEMO

    您可以将其用作

    double mu = 1.0;
    double w0 = 1.0;
    std::function<double(double, double)> f
        = [mu, w0] (double x, double y) { return func_to_integrate(mu, w0, x, y); };
    
    integrator(get_function_ptr(f));
    

    但请注意,您在此处理全局变量。这通常有效,但有时可能会导致细微的错误(例如,当您在单个表达式中多次调用get_function_ptr时)。

答案 1 :(得分:1)

  

如何将func_to_integrate等函数传递给积分器?

似乎很容易修复。只需在指针函数签名中再添加两个参数。

double integrator(double (*func_to_integrate)(double,double,double,double));

答案 2 :(得分:0)

如前所述,最优雅的解决方案是使用bind和or lambda。一个很好的解决方案是适配器设计模式类包装器,其中mu和w0成为类成员。

class IntegratorAdaptor {
  private:
    double _mu, double _w0;

  public:
    IntegratorAdapter(double arg_mu, double arg_w0)
    : _mu(arg_mu), _w0(arg_w0) { }

    double twoArgIntegrator( double x, double y )
      { return func_to_intergrate( _mu, _w0, x, y ); }
};

这个类的构造开销非常低,所以我使成员不可变。我没有为这个类和函数提供非常好的名字,你应该比我更多考虑这些名字。

答案 3 :(得分:0)

我在这类问题上看到的大多数答案都依赖于std::function和/或C ++模板。我想分享一个可能不太通用的替代解决方案,但对我来说更简单。 它不使用std::function或模板---事实上,它根本不使用任何库。

这个想法是,不是传递一个函数指针,而是传递一个实现特定“接口”的对象。在这个例子中,

double integrator(double (*func_to_integrate)(double,double))

变为

double integrator(Integratable func_to_integrate)

其中Integratable是定义为

的'接口'(抽象基类)
class Integratable {
public:    
    virtual double compute(double x, double y) = 0;  // pure virtual function
}

然后我们可以将func_to_integrate放入此类的实例中,并为其他参数添加额外成员:

class SomeClassName : public Integratable {
public:
    double compute(double x, double y);
    double mu;
    double w0;
}

SomeClassName func_to_integrate;

在循环中测试muw0的多个值:

for(double mu : mus) {
    for(double w0 : w0s) {
        func_to_integrate.mu = mu;
        func_to_integrate.w0 = w0;
        integrator(func_to_integrate);
    }
}

当然,我们必须修改integrator,这样它就不会调用函数指针,而是调用传递给它的对象上的compute()方法,但这很简单(假设你可以改变它) integrator的签名,这可能是解决此问题的可能方法所必需的。)

我喜欢这个解决方案,因为它避免了一些C ++更重量级的功能和库。但是,它肯定不如通常建议在C ++中部分应用的许多其他解决方案一般。对于OP,我相信这个解决方案非常适合给定的用例。