不仅仅是一般情况,我有一个非常具体的例子:在GSL(GNU Scientific Library)中,使用的主要函数类型(为了执行集成,根查找,......)是{{1} },其类型为gsl_function
function
说我想从double(*)(double, void *)
创建gsl_function
。 double a_squared(double a) {return a*a};
的类型为a__squared
我想创建一个double(*)(double)
函数,接受参数convert
并返回类型为(double(*)(double) f)
的对象,该对象将满足double(*)(double, void *)
1}}
但经过一些研究后,我似乎无法在convert(f)(double a, NULL) == f(a)
函数中定义另一个函数。怎么办?
答案 0 :(得分:4)
将原始函数指针传递给GSL API的需求极大地限制了您的选项 - 您无法使用基于std::function
的任何内容,因为无法从std::function
获取函数指针(并且这会使用捕获来排除lambda,这本来可以提供一个简洁的解决方案。)
鉴于这些限制,这里有一个使用静态包装类的可能解决方案。您也可以将此类的内容放在命名空间中,但使用该类至少会给出一些封装外观。
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
答案 1 :(得分:1)
你被gsl(差)设计选择使用C(而不是C ++)来提供C风格的函数指针。因此,您不能使用(C ++样式)函数对象(仿函数),但必须提供指向实际函数的指针,并且无法生成函数,就像生成仿函数一样。
(不推荐)您可以使用全局变量来存储实际函数(a_squared
),然后定义实际调用该全局变量的特定gsl_function
: / p>
// 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
隐藏为某些类的静态成员,如atkins的答案仍需要全局变量)。这是有效的,但很差,因为1)这个机制需要一个全局变量,2)只允许任何时候使用一个目标函数(这可能很难确保)。
(推荐)但是,您可以定义一个特殊函数,它将另一个函数指针作为参数并将其作为数据元素传递。这实际上是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库。
答案 2 :(得分:1)
以为我会在此基础上添加基于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无法转换为简单的函数指针,这是函数返回值所要求的。为了能够返回这个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用这个想法指出的问题。
答案 3 :(得分:1)
以前的答案 - 包括已接受的答案 - 似乎是正确的,但如果您需要将其他类型的函数转换为gsl_function(例如包括成员函数),则它们不够通用。所以,让我添加一个更强大的替代方案。
如果使用here描述的包装器,那么你可以用两个简单的行将任何C ++ lambdas转换为gsl_functions
{{1}}
这解决了任何相关的转换问题。您还可以使用std :: bind和任何std :: functions。