通过type(c_ptr)虚拟变量传递外部函数和自定义数据类型

Passing external function and self-defined data type through type(c_ptr) dummy variable

我想使用 GSL with the Fortran interface FGSL in order to use the provided multiroots solver. In order to run the simulation I need to define the residual of the root finding problem. In the tests of FSGL there is this Rosenbrock example:

function rosenbrock_f(x, params, f) bind(c)
  type(c_ptr), value :: x, params, f
  integer(c_int) :: rosenbrock_f
  !
  type(fgsl_vector) :: fx, ff
  real(fgsl_double), pointer :: par(:), xv(:), yv(:)
  integer(fgsl_int) :: status
  ! 
  call fgsl_obj_c_ptr(fx, x)
  call fgsl_obj_c_ptr(ff, f)
  call c_f_pointer(params, par, (/ 2 /))
  status = fgsl_vector_align(xv, fx)
  status = fgsl_vector_align(yv, ff)
  yv(1) = par(1) * (1.0_fgsl_double - xv(1))
  yv(2) = par(2) * (xv(2) - xv(1)*xv(1))
  rosenbrock_f = fgsl_success
end function rosenbrock_f

这里x是输入变量,params是附加变量,f是输出残差。在我的代码中,我已经有一个残差函数,所以我想通过参数将它作为外部函数传递。不幸的是,我还需要传递另一种自定义数据类型,同样是通过params。我需要这样,因为 GSL 的求解器需要具有正确虚拟变量的函数,我不知道如何实现。

所以我的问题是:
如何通过参数传递外部函数和自定义数据类型以便在此函数中使用它?

改变上面的例子让我

function rosenbrock_f(x, params, f) bind(c)
  type(c_ptr), value :: x, params, f
  integer(c_int) :: rosenbrock_f
  !
  type(fgsl_vector) :: fx, ff
  real(fgsl_double), pointer :: xv(:), yv(:) ! par removed
  integer(fgsl_int) :: status
  ! NEW DECLARATIONS
  type(owntype) :: this
  external :: GetRes
  ! 
  call fgsl_obj_c_ptr(fx, x)
  call fgsl_obj_c_ptr(ff, f)

  ! In this region I need to get GetRes (external function)
  ! and this (self-defined data type) from the params variable
  ! instead of par
  call c_f_pointer(params, GetRes))
  !call c_f_pointer(params, this)) ! not possible

  status = fgsl_vector_align(xv, fx)
  status = fgsl_vector_align(yv, ff)

  call GetRes(this,xv,yv) ! call the external function

  rosenbrock_f = fgsl_success
end function rosenbrock_f

但是,这只会让我访问 GetRes。我有一些想法,比如形状为 2 的参数指针。与上面类似,但允许不同的数据类型。

编辑 1(更多代码): 这是它的更多代码。外部函数会像这样查找上面的示例。

subroutine GetRes(this,y,res)
  implicit none
  type(owntype)    :: this
  double precision, dimension(this%neq) :: y
  double precision, dimension(this%neq) :: res
  intent (in)     :: y
  intent (out)    :: res

  res(1) = (1.0 - y(1))
  res(2) = (y(2) - y(1)*y(1))

end subroutine GetRes

为了看函数rosenbrock_f是如何使用的我认为最好是参考multiroots tests.

解算器由 line 80:

初始化
mroot_f = fgsl_multiroot_function_init(rosenbrock_f,nrt,ptr)

设置在line 84

status = fgsl_multiroot_fsolver_set(mroot_fslv, mroot_f, xvec)

然后在line 97中迭代调用:

status = fgsl_multiroot_fsolver_iterate(mroot_fslv);

实际上问题多于答案,但是...

你能只定义一个包含你需要的所有 C 指针的类型吗?

type, bind(C) :: T
   type(C_PTR) this
   type(C_FUNPTR) GetRes
end type T

然后你可以创建一个 T 的实例并将它的 thisGetRes 指针指向你想要的东西,然后你有

type(C_PTR) par
!...
par = C_LOC(T_instance)

所以现在 par 将全部加载,在你的最终函数中你有声明

type(C_PTR), value :: params
type(T), pointer :: T_instance
type(owntype), pointer :: this
procedure(whatever), pointer :: GetRes

然后是序列

call C_F_POINTER(params,T_instance)
call C_F_POINTER(T_instance%this,this)
call C_F_PROCPOINTER(T_instance%GetRes, GetRes)

还是我对你的问题的解释太简单了?