从c++调用带有可选参数的Fortran子程序

Calling Fortran subroutines with optional arguments from C++

本文关键字:参数 Fortran 子程序 c++ 调用      更新时间:2023-10-16

我如何在使用可选参数的c++头文件中引用Fortran函数?我是否有一个原型在头为每个可能的组合调用?或者这可能吗?

例如,Fortran:

subroutine foo(a, b, c) bind(c)
   real, intent(in), optional :: a, b, c
   ...
end subroutine foo

这是不可能的,至少是可移植的,除非您使子程序bind(C)

一旦你把它设置为bind(C),它只是传递一个指针,在C端可以是NULL。

subroutine foo(a, b, c) bind(C, name="foo")
   use iso_c_binding, only: c_float
   real(c_float), intent(in), optional :: a, b, c
   ...
end subroutine foo

(为了更好的可移植性,我使用了iso_c_binding模块中的real(c_float),但这与这个问题有些无关)

C (+ +)

extern "C"{
  void foo(float *a, float *b, float *c);
}
foo(&local_a, NULL, NULL);

,然后你可以创建一个c++函数,它调用foo并使用c++风格的可选参数。

此功能在Fortran技术规范ISO/IEC TS 29113:2012中允许Fortran与C的进一步互操作性,后来被纳入Fortran 2018。

作为Vladimir F回答,在Fortran 2018(和Fortran 2008+TS29113)下,可以在C可互操作的Fortran过程中使用optional属性作为虚拟参数。

在Fortran 2008下这是不可能的。一些编译器目前仍不支持此特性。使用这些编译器,仍然可以(尽管需要更多的工作)支持"可选"参数。

问题的过程foo在F2008下不能与c互操作(即使与bind(C))。然而,在F2008下模仿这个想法是可能的:用type(c_ptr)参数包装所需的Fortran过程,有一个c可互操作的过程。这个可互操作的包装器可以检查空指针(使用C_ASSOCIATED),以确定是否存在向前传递的参数-如果存在,则传递未引用的参数。

例如,具有c互操作包装器的Fortran端可能看起来像

module mod
  use, intrinsic :: iso_c_binding
contains
  subroutine foo_centry(a) bind(c,name='foo')
    type(c_ptr), value :: a
    real(c_float), pointer :: a_pass
    nullify(a_pass)
    if (c_associated(a)) call c_f_pointer(a, a_pass)
    call foo(a_pass)
  end subroutine foo_centry
  subroutine foo(a)
    real(c_float), optional :: a
  end subroutine foo
end module mod

在Fortran 2018中,我们在可互操作接口中有这种对称性:如果过程是由Fortran以外的方式定义的,但是可互操作接口有一个可选参数,那么在F2018中,我们有这样的结果:引用这个过程时不存在参数意味着空指针被传递给了过程。

在F2008下,我们也需要处理这方面:我们再次使用F2008非互操作过程来处理这个问题,它用type(c_ptr)参数包装了一个可互操作的过程:如果参数存在,传递它的地址;如果没有,传递C_NULL_PTR

这样的F2008代码可能看起来像

module mod
  use, intrinsic :: iso_c_binding
  interface
     subroutine foo_fentry(a) bind(c,name='foo')
       import c_ptr
       type(c_ptr), value :: a
     end subroutine foo_fentry
  end interface
contains
  subroutine foo(a)
    real(c_float), optional, target :: a
    if (present(a)) then
       call foo_fentry(c_loc(a))
    else
       call foo_fentry(c_null_ptr)
    end if
  end subroutine foo
end module mod

请注意使用c_loc所造成的限制:在某些情况下,可能需要使用副本或采取其他保护措施。