从另一个函数创建一个函数

Create a function from another one

本文关键字:函数 一个 另一个 创建      更新时间:2023-10-16

不仅仅是一般情况,我有一个非常具体的例子:在GSL (GNU科学库)中,使用的主要函数类型(为了执行积分,根查找,…)是gsl_function,它有一个属性function,其类型是double(*)(double, void *)

假设我想从double a_squared(double a) {return a*a};创建一个gsl_functiona__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,这将提供一个整洁的解决方案)。

考虑到这些约束,这里有一个使用静态包装器类的可能解决方案。您也可以将该类的内容放在名称空间中,但使用该类至少可以提供一些封装的外观。

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++风格)函数对象(functor),而必须提供指向实际函数的指针,并且不能以生成functor的相同方式生成函数。

(不推荐)您可以使用一个全局变量来存储实际的函数(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作为某个类的静态成员,如在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库。

我想我会在这里添加基于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在那个想法中指出的问题。

前面的答案—包括被接受的答案—似乎是正确的,但是如果您需要将其他类型的函数转换为gsl_function(例如包括成员函数),它们就不够通用。所以,让我添加一个更强大的选项。

如果您使用这里描述的包装器,那么您可以通过两行简单的代码将任何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::函数。

相关文章: