这是一个非常基本的问题,我确信之前已经回答过,但我不知道该搜索什么。
陈述我有一个集成数学函数的函数:
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操作?
答案 0 :(得分:2)
鉴于您可以更改integrator
功能的签名,有几种解决方案。基本的两个方向是
std::function<double(double, double)>
作为函数参数。两种选择都允许您传递一般函数对象(仿函数,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();
}
您可以将其用作
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;
在循环中测试mu
和w0
的多个值:
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,我相信这个解决方案非常适合给定的用例。