从另一个创建一个函数

时间:2015-10-26 15:50:37

标签: c++ c gsl

不仅仅是一般情况,我有一个非常具体的例子:在GSL(GNU Scientific Library)中,使用的主要函数类型(为了执行集成,根查找,......)是{{1} },其类型为gsl_function

的属性function

说我想从double(*)(double, void *)创建gsl_functiondouble 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)函数中定义另一个函数。怎么办?

4 个答案:

答案 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。