【问题标题】:Pass pointer-to-member-function for class without copy constructor into Fortran将没有复制构造函数的类的指向成员函数的指针传递到 Fortran
【发布时间】:2018-07-17 10:04:25
【问题描述】:

我有一个 Fortran 函数的调用,它的第一个参数是一个函数,我想为此使用 C++ 成员函数的形式,

void Obj::memberF(double* x,double* res)

Obj 禁用了复制构造函数(这变得很重要,如下所示)。

Fortran 子程序的格式为

subroutine solve(f,len,x)

f 有两个参数,xrhs,它们都是实数数组。 len 代表这些数组的长度。

(本质上,需要在非线性求解器库中调用残差函数)

为此,无论最终的 C++ 方法如何,我们都需要一个活页夹,如下所示

binder.f90

SUBROUTINE cpp_solve(cpp_f,len,x) BIND(C, NAME='cpp_solve')
  USE, INTRINSIC :: iso_c_binding, ONLY : c_int, c_float
  INTERFACE
    SUBROUTINE cpp_f(x,rhs) BIND(C)
      USE, INTRINSIC :: iso_c_binding, ONLY : c_float
      REAL(c_float) :: x(:)
      REAL(c_float) :: rhs(:)
    END SUBROUTINE cpp_f
  END INTERFACE
  INTEGER(c_int) :: len
  REAL(c_float) :: x(len)
  CALL solve(cpp_f,len,x)
END SUBROUTINE cpp_solve

对于 C++ 程序,首先,这些函数需要一个包装器

wrap.h

extern "C" {
  void cpp_f(double*,double*);
  void cpp_solve(void (Obj::*cpp_f)(double*,double*),int*,double*);
}

主程序如下

main.cpp

#include "wrap.h"

int main {
  int len = 2;
  std::vector<double> x(len);
  // Multiple lines to initialize foo
  // Creating a global Obj is not feasible
  // ...
  Obj current_object(foo);

  // WHAT GOES HERE? 

  // cpp_solve(...,len,x);
}  

以下是我考虑过的几种方法,从最近的 C++ 功能开始,

1) 请注意,Obj 的复制构造函数由于其他原因已被禁用,并且是约束。这使我无法使用std::bind 附加当前实例并获取函数指针。

2) 另外,定义另一个函数 extra_function(Obj&amp; obj, double*,...) 然后使用 lambda 只返回 obj.memberF 也不是一个选项,因为这需要我指定一个捕获,给定自定义对象并且我需要一个函数指针仅限。

3) 一个更简单的选择是只获取成员函数指针,然后按如下方式传递当前实例

typedef void (Obj::*fptr)(double*,double*);

fptr current_fPointer = &Obj::memberF;
cpp_solve(current_object.*current_fPointer,len,x);

这给了我“无效使用非静态成员函数”错误。

我可以使用其他方法来获取函数指针吗?

TBH,我实际上是在尝试使用这些 C++11 功能调用 Fortran 77 例程,所以这是一些复古主义。

【问题讨论】:

  • 如果您希望cpp_f 与当前的 Fortran 标准可互操作,则不能假定虚拟参数的形状。
  • 是的,很抱歉。
  • 您是否考虑过使用由 lambda 初始化的 std::function,例如:std::function&lt;void(double* , double*)&gt; foo = [&amp;current_object](double* len, double* x) { current_object.memberF(len, x); };
  • @francescalus 你对如何将数组长度传递给 cpp_f 中的参数有什么建议吗?原型无法更改,因为它是库的一部分。
  • 如果这是 cpp_f 的准确接口,那么您可以在 this question 中找到此类函数如何可互操作的示例。

标签: c++ c++11 fortran function-pointers


【解决方案1】:

如果这是一个单线程程序,实用的方法可以使用带有额外函数的全局指针变量

static Obj *g_obj;

static void extra_func(double *x1, double *x2)
{
    g_obj->memberF(x1, x2);
}

void call_f77(Obj *o1, int *len, double *x)
{
    g_obj = o1;
    cpp_solve(extra_func, len, x);
}

另一种方法,如果您可以修改 Fortran 代码(并且 Fortran 可以透明地以某种方式传递对象指针),您可以通过 Fortran 将相关对象隧道传输到 extra_func,例如

static void extra_func(void *p, double *x1, double *x2)
{
    Obj *obj = static_cast<Obj*>(p);
    obj->memberF(x1, x2);
}

void call_f77(Obj *o1, int *len, double *x)
{
    cpp_solve(extra_func, o1, len, x);
}

在 Fortran 中(请原谅我,我从未见过任何 Fortran 代码):

SUBROUTINE solve(cpp_f, obj, len, x)
...
CALL cpp_f(obj, x, rhs)
...

【讨论】:

  • 感谢您的方法,我实际上已经考虑过了。不幸的是,我不能创建一个全局的Obj,因为它需要foo,这涉及到多个要初始化的对象,它们构成了程序的大部分。我已经编辑了我的问题以澄清这一点。
  • 不是全局对象,而是指向当前使用/活动对象的全局指针。
  • 值得一试。不过,考虑到全局变量的存在,我会等待其他答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-27
  • 2015-03-02
  • 1970-01-01
  • 2023-03-22
  • 2020-11-08
  • 2018-09-20
  • 2010-10-31
相关资源
最近更新 更多