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

10

如何在使用可选参数的C++头文件中引用Fortran函数?我需要为每个可能的调用组合在头文件中提供原型吗?或者这是不可能的吗?

例如,Fortran:

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

13

除非您将子程序 bind(C),否则至少在可移植性方面是不可能的。

一旦您将其设置为 bind(C),它只是传递一个指针,在C端可以为空。

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);

然后您可以编写一个调用 foo 并使用C++风格可选参数的C++函数。

在技术规范ISO/IEC TS 29113:2012中,Fortran允许这种能力与C进一步互操作,并在随后并入了Fortran 2018。


确实,如果没有使用bind(c),它就无法正常工作。谢谢! - DavidH

6
Vladimir F answers所述,在Fortran 2018(和Fortran 2008+TS29113)中,可以在C可互操作的Fortran过程中使用optional属性作为虚拟参数。
在Fortran 2008中不可能做到这一点。目前仍有几个编译器不支持此功能。对于这些编译器,人们仍然可以(尽管需要更多工作)支持“可选”参数。
问题中的foo过程在F2008下不是C可互操作的(即使使用bind(C))。但是,在F2008下可以模仿这个想法:使用type(c_ptr)参数创建一个C可互操作的过程,该过程包装所需的Fortran过程。这个互操作包装器可以使用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下,引用该过程且未提供该参数意味着将null指针传递给过程。
在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可能会受到限制:对于某些情况,您可能希望使用副本或采取其他保护措施。


非常感谢您提供这么详细的解释,并提供了如何使用F2008编译器进行操作的细节。为了确保如何在F2008中实现:如果我想从Fortran调用一个C函数foo_c(int* val)并支持可选参数,我首先会创建一个子程序foo_f(val) bind(c, name='foo_c'),其中type(c_ptr), value, intent(in) :: val。然后包装子程序是foo_f_wrap(val),其中integer, intent(in), target, optional :: val,如果val存在,则调用fun_f(c_loc(val)),否则调用fun_f(c_null_ptr)。我的理解正确吗? - Michael Schlottke-Lakemper
我已经添加了示例,每个包装方向都有一个。 - francescalus
太好了。我认为这真正清楚地解释了任何查看此问题并考虑C->F和F->C调用方向的人。 - Michael Schlottke-Lakemper

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接