从另一个函数创建一个函数
Create a function from another one
除了一般情况,我还有一个非常具体的例子:在 GSL(GNU 科学图书馆)中,使用的主要函数类型(为了执行积分、求根等)是 gsl_function
,它有一个属性 function
,其类型是 double(*)(double, void *)
假设我想从 double a_squared(double a) {return a*a};
创建一个 gsl_function
。 a__squared
的类型是 double(*)(double)
我想创建一个 convert
函数接受参数 (double(*)(double) f)
并返回一个满足 double(*)(double, void *)
类型的对象 convert(f)(double a, NULL) == f(a)
但经过一些研究,我似乎无法在我的 convert
函数中定义另一个函数。如何进行?
将原始函数指针传递给 GSL 的需要 API 极大地限制了您的选择 - 您不能使用基于 std::function
的任何内容,因为无法从std::function
(这排除了使用捕获的 lambda,这将提供一个简洁的解决方案)。
考虑到这些限制,这里有一个使用静态包装器的可能解决方案 class。您也可以将此 class 的内容放在命名空间中,但使用 class 至少提供了一些封装的外观。
typedef double gsl_function_type(double, void*); // typedef to make things a bit more readable...
// static class to wrap single-parameter function in GSL-compatible interface
// this really just serves as a namespace - there are no non-static members,
// but using a class lets us keep the details private
class Convert
{
Convert() = delete; // don't allow construction of this class
// pointer to the function to be invoked
static double (*m_target)(double);
// this is the function we'll actually pass to GSL - it has the required signature
static double target(double x, void*) {
return m_target(x); // invoke the currently wrapped function
}
public:
// here's your "convert" function
static gsl_function_type* convert(double (*fn)(double)) {
m_target = fn;
return ⌖
}
};
这里有一个实例:http://coliru.stacked-crooked.com/a/8accb5db47a0c51d
您被 gsl 的(糟糕的)设计选择所困,即使用 C(而不是 C++)来提供 C 风格的函数指针。因此,您不能使用(C++ 风格)函数对象(仿函数),但必须提供指向真实函数的指针,并且不能像生成仿函数那样生成函数。
(不推荐) 您可以使用全局变量来存储实际函数 (a_squared
),然后定义一个特定的 gsl_function
实际调用该全局变量:
// from some gsl header:
extern "C" {
typedef double gsl_function(double, void*);
// calls func(arg,data_passed_to_func)
double gsl_api_function(gsl_function*func, void*data_passed_to_func);
}
// in your source code
double(*target_func)(double); // global variable can be hidden in some namespace
extern "C" {
double funtion_calling_target(double, void*)
}
double funtion_calling_target(double arg, void*)
{
return target_func(arg);
}
bool test(double x, double(*func)(double))
{
target_func = func;
return x < gsl_api_function(function_calling_target,0);
}
(隐藏 target_func
作为某些 class 的静态成员,如阿特金斯的回答仍然需要一个全局变量)。这行得通,但是很差,因为 1) 这种机制需要一个全局变量,并且 2) 只允许在任何时候使用一个目标函数(这可能很难确保)。
(Recommended) 但是,您可以定义一个特殊函数,将另一个函数指针作为参数并将其作为数据元素传递。这实际上是 gsl_function
设计背后的想法:void*
可以指向函数可能需要的任何辅助数据。这样的数据可以是另一个函数。
// your header
extern "C" {
double function_of_double(double, void*);
}
inline double function_of_double(double arg, void*func)
{
typedef double(*func_of_double)(double);
return reinterpret_cast<func_of_double>(func)(arg);
}
// your application
bool test(double x, double(*func)(double))
{
return x < gsl_api_function(function_of_double, (void*)(func));
}
这不需要全局变量,并且可以根据需要使用任意多个不同的同步函数。当然,在这里你正在搞乱 void*
,这是每个明智的 C++ 程序员都厌恶的东西,但是你正在使用一个基于 void*
操作的可怕的 C 库。
我想我会在此添加基于 lambda 的尝试。
原则上没问题:
// function we want to pass to GSL
double a_squared(double a) { return a*a; }
typedef double gsl_function_type(double, void*); // convenient typedef
// lambda wrapping a_squared in the required interface: we can pass f directly to GSL
gsl_function_type* f = [](double x, void*) { return a_squared(x); };
但我们真的很想编写一个方法来将其应用于任何给定的函数。像这样:
gsl_function_type* convert(double (*fn)(double))
{
// The lambda has to capture the function pointer, fn.
return [fn](double x, void*) { return fn(x); };
}
但是,lambda 现在必须捕获指针 fn
,因为 fn
具有自动存储持续时间(与第一个示例中的静态函数 a_squared
相反)。这不会编译,因为使用捕获的 lambda 不能转换为简单的函数指针,正如我们函数的 return 值所要求的那样。为了能够 return 这个 lambda,我们必须使用 std::function
,但是没有办法从中获取原始函数指针,所以这里没有用。
所以我设法让它工作的唯一方法是使用预处理器宏:
#define convert(f) [](double x, void*) { return f(x); }
然后让我这样写:
#include <iostream>
using namespace std;
typedef double gsl_function_type(double, void*); // convenient typedef
// example GSL function call
double some_gsl_function(gsl_function_type* function)
{
return function(5.0, nullptr);
}
// function we want to pass to GSL
double a_squared(double a) { return a*a; }
// macro to define an inline lambda wrapping f(double) in GSL signature
#define convert(f) [](double x, void*) { return f(x); }
int main()
{
cout << some_gsl_function(convert(a_squared)) << endl;
}
就个人而言,尽管我不喜欢使用宏,但我更喜欢这个而不是我的其他建议。特别是,它解决了@Walter 用那个想法指出的问题。
以前的答案 - 包括已接受的答案 - 似乎是正确的,但它们不够通用,以防你需要将其他类型的函数转换为 gsl_function(例如包括成员函数)。所以,让我添加一个更强大的替代方案。
如果您使用 here 描述的包装器,那么您可以在两行简单的代码中将任何 C++ lambdas 转换为 gsl_functions
// Example
gsl_function_pp Fp([&](double x){return a_squared(x);});
gsl_function *F = static_cast<gsl_function*>(&Fp);
这解决了所有相关的转换问题。您还可以使用 std::bind 和任何 std::functions。
除了一般情况,我还有一个非常具体的例子:在 GSL(GNU 科学图书馆)中,使用的主要函数类型(为了执行积分、求根等)是 gsl_function
,它有一个属性 function
,其类型是 double(*)(double, void *)
假设我想从 double a_squared(double a) {return a*a};
创建一个 gsl_function
。 a__squared
的类型是 double(*)(double)
我想创建一个 convert
函数接受参数 (double(*)(double) f)
并返回一个满足 double(*)(double, void *)
类型的对象 convert(f)(double a, NULL) == f(a)
但经过一些研究,我似乎无法在我的 convert
函数中定义另一个函数。如何进行?
将原始函数指针传递给 GSL 的需要 API 极大地限制了您的选择 - 您不能使用基于 std::function
的任何内容,因为无法从std::function
(这排除了使用捕获的 lambda,这将提供一个简洁的解决方案)。
考虑到这些限制,这里有一个使用静态包装器的可能解决方案 class。您也可以将此 class 的内容放在命名空间中,但使用 class 至少提供了一些封装的外观。
typedef double gsl_function_type(double, void*); // typedef to make things a bit more readable...
// static class to wrap single-parameter function in GSL-compatible interface
// this really just serves as a namespace - there are no non-static members,
// but using a class lets us keep the details private
class Convert
{
Convert() = delete; // don't allow construction of this class
// pointer to the function to be invoked
static double (*m_target)(double);
// this is the function we'll actually pass to GSL - it has the required signature
static double target(double x, void*) {
return m_target(x); // invoke the currently wrapped function
}
public:
// here's your "convert" function
static gsl_function_type* convert(double (*fn)(double)) {
m_target = fn;
return ⌖
}
};
这里有一个实例:http://coliru.stacked-crooked.com/a/8accb5db47a0c51d
您被 gsl 的(糟糕的)设计选择所困,即使用 C(而不是 C++)来提供 C 风格的函数指针。因此,您不能使用(C++ 风格)函数对象(仿函数),但必须提供指向真实函数的指针,并且不能像生成仿函数那样生成函数。
(不推荐) 您可以使用全局变量来存储实际函数 (a_squared
),然后定义一个特定的 gsl_function
实际调用该全局变量:
// from some gsl header:
extern "C" {
typedef double gsl_function(double, void*);
// calls func(arg,data_passed_to_func)
double gsl_api_function(gsl_function*func, void*data_passed_to_func);
}
// in your source code
double(*target_func)(double); // global variable can be hidden in some namespace
extern "C" {
double funtion_calling_target(double, void*)
}
double funtion_calling_target(double arg, void*)
{
return target_func(arg);
}
bool test(double x, double(*func)(double))
{
target_func = func;
return x < gsl_api_function(function_calling_target,0);
}
(隐藏 target_func
作为某些 class 的静态成员,如阿特金斯的回答仍然需要一个全局变量)。这行得通,但是很差,因为 1) 这种机制需要一个全局变量,并且 2) 只允许在任何时候使用一个目标函数(这可能很难确保)。
(Recommended) 但是,您可以定义一个特殊函数,将另一个函数指针作为参数并将其作为数据元素传递。这实际上是 gsl_function
设计背后的想法:void*
可以指向函数可能需要的任何辅助数据。这样的数据可以是另一个函数。
// your header
extern "C" {
double function_of_double(double, void*);
}
inline double function_of_double(double arg, void*func)
{
typedef double(*func_of_double)(double);
return reinterpret_cast<func_of_double>(func)(arg);
}
// your application
bool test(double x, double(*func)(double))
{
return x < gsl_api_function(function_of_double, (void*)(func));
}
这不需要全局变量,并且可以根据需要使用任意多个不同的同步函数。当然,在这里你正在搞乱 void*
,这是每个明智的 C++ 程序员都厌恶的东西,但是你正在使用一个基于 void*
操作的可怕的 C 库。
我想我会在此添加基于 lambda 的尝试。
原则上没问题:
// function we want to pass to GSL
double a_squared(double a) { return a*a; }
typedef double gsl_function_type(double, void*); // convenient typedef
// lambda wrapping a_squared in the required interface: we can pass f directly to GSL
gsl_function_type* f = [](double x, void*) { return a_squared(x); };
但我们真的很想编写一个方法来将其应用于任何给定的函数。像这样:
gsl_function_type* convert(double (*fn)(double))
{
// The lambda has to capture the function pointer, fn.
return [fn](double x, void*) { return fn(x); };
}
但是,lambda 现在必须捕获指针 fn
,因为 fn
具有自动存储持续时间(与第一个示例中的静态函数 a_squared
相反)。这不会编译,因为使用捕获的 lambda 不能转换为简单的函数指针,正如我们函数的 return 值所要求的那样。为了能够 return 这个 lambda,我们必须使用 std::function
,但是没有办法从中获取原始函数指针,所以这里没有用。
所以我设法让它工作的唯一方法是使用预处理器宏:
#define convert(f) [](double x, void*) { return f(x); }
然后让我这样写:
#include <iostream>
using namespace std;
typedef double gsl_function_type(double, void*); // convenient typedef
// example GSL function call
double some_gsl_function(gsl_function_type* function)
{
return function(5.0, nullptr);
}
// function we want to pass to GSL
double a_squared(double a) { return a*a; }
// macro to define an inline lambda wrapping f(double) in GSL signature
#define convert(f) [](double x, void*) { return f(x); }
int main()
{
cout << some_gsl_function(convert(a_squared)) << endl;
}
就个人而言,尽管我不喜欢使用宏,但我更喜欢这个而不是我的其他建议。特别是,它解决了@Walter 用那个想法指出的问题。
以前的答案 - 包括已接受的答案 - 似乎是正确的,但它们不够通用,以防你需要将其他类型的函数转换为 gsl_function(例如包括成员函数)。所以,让我添加一个更强大的替代方案。
如果您使用 here 描述的包装器,那么您可以在两行简单的代码中将任何 C++ lambdas 转换为 gsl_functions
// Example
gsl_function_pp Fp([&](double x){return a_squared(x);});
gsl_function *F = static_cast<gsl_function*>(&Fp);
这解决了所有相关的转换问题。您还可以使用 std::bind 和任何 std::functions。