将 n 个参数的函数作为 n-x 个参数的函数传递
Pass a function of n arguments as function of n-x arguments
这是一个非常基本的问题,我确定之前有人回答过这个问题,但我不知道要搜索什么。
声明我有一个集成了数学函数的函数:
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 之类的函数传递给集成商?
问候
编辑:正如 alain 在评论中指出的,这部分重复了:How can currying be done in C++?
有没有一个对函数指针进行柯里化操作的优雅解决方案?
How can I pass a function like func_to_integrate to integrator?
看起来很容易修复。只需在指针函数签名中再添加两个参数即可。
double integrator(double (*func_to_integrate)(double,double,double,double));
正如之前的评论所指出的,最优雅的解决方案是使用 bind 和/或 lambda。一个不错的解决方案是适配器设计模式 class 包装器,其中 mu 和 w0 成为 class 成员。
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 ); }
};
此 class 的构造开销非常低,因此我将成员设为不可变。我没有为 class 和函数想出很好的名字,你应该比我想出更多的名字。
鉴于您能够更改 integrator
函数的签名,有多种解决方案。基本的两个方向是
- 使用通用模板参数而不是函数指针(--调用者必须知道要传递的正确签名),或者
- 使用
std::function<double(double, double)>
作为函数参数。
这两种选择都允许您传递通用函数对象(函子、lambda、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
时)。
我看到的大多数此类问题的答案都依赖于 std::function
and/or C++ 模板。我想分享一个可能不太通用的替代解决方案,但对我来说更简单。 它不使用 std::function
或模板——事实上,它根本不使用任何库。
这个想法是传递一个实现特定 'interface' 的对象,而不是传递一个函数指针。在这个例子中,
double integrator(double (*func_to_integrate)(double,double))
变成
double integrator(Integratable func_to_integrate)
其中 Integratable
是一个 'interface'(抽象基础 class)定义为
class Integratable {
public:
virtual double compute(double x, double y) = 0; // pure virtual function
}
然后我们可以将 func_to_integrate
变成这个 class 的一个实例,为额外的参数添加额外的成员:
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,我相信这个解决方案非常适合给定的用例。
这是一个非常基本的问题,我确定之前有人回答过这个问题,但我不知道要搜索什么。
声明我有一个集成了数学函数的函数:
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 之类的函数传递给集成商?
问候
编辑:正如 alain 在评论中指出的,这部分重复了:How can currying be done in C++?
有没有一个对函数指针进行柯里化操作的优雅解决方案?
How can I pass a function like func_to_integrate to integrator?
看起来很容易修复。只需在指针函数签名中再添加两个参数即可。
double integrator(double (*func_to_integrate)(double,double,double,double));
正如之前的评论所指出的,最优雅的解决方案是使用 bind 和/或 lambda。一个不错的解决方案是适配器设计模式 class 包装器,其中 mu 和 w0 成为 class 成员。
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 ); }
};
此 class 的构造开销非常低,因此我将成员设为不可变。我没有为 class 和函数想出很好的名字,你应该比我想出更多的名字。
鉴于您能够更改 integrator
函数的签名,有多种解决方案。基本的两个方向是
- 使用通用模板参数而不是函数指针(--调用者必须知道要传递的正确签名),或者
- 使用
std::function<double(double, double)>
作为函数参数。
这两种选择都允许您传递通用函数对象(函子、lambda、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
时)。
我看到的大多数此类问题的答案都依赖于 std::function
and/or C++ 模板。我想分享一个可能不太通用的替代解决方案,但对我来说更简单。 它不使用 std::function
或模板——事实上,它根本不使用任何库。
这个想法是传递一个实现特定 'interface' 的对象,而不是传递一个函数指针。在这个例子中,
double integrator(double (*func_to_integrate)(double,double))
变成
double integrator(Integratable func_to_integrate)
其中 Integratable
是一个 'interface'(抽象基础 class)定义为
class Integratable {
public:
virtual double compute(double x, double y) = 0; // pure virtual function
}
然后我们可以将 func_to_integrate
变成这个 class 的一个实例,为额外的参数添加额外的成员:
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,我相信这个解决方案非常适合给定的用例。