【问题标题】:Calling Fortran subroutines with optional arguments from C++从 C++ 调用带有可选参数的 Fortran 子例程
【发布时间】:2017-02-26 15:00:00
【问题描述】:

如何在使用可选参数的 C++ 标头中引用 Fortran 函数?对于每个可能的调用组合,我会在标题中放置一个原型吗?或者这甚至可能吗?

例如,Fortran:

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

【问题讨论】:

    标签: c++ fortran optional-parameters fortran-iso-c-binding


    【解决方案1】:

    这是不可能的,至少是可移植的,除非你创建子程序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);
    

    然后您可以创建一个调用 foo 并使用 C++ 样式可选参数的 C++ 函数。

    Fortran 在技术规范 ISO/IEC TS 29113:2012 中允许使用此功能,以进一步实现 Fortran 与 C 的互操作性,后来被合并到 Fortran 2018 中。

    【讨论】:

    • 确实,如果没有 bind(c),它不会去任何地方。谢谢!
    【解决方案2】:

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

    在 Fortran 2008 下这是不可能的。目前仍有几个编译器不支持此功能。使用这些编译器,一个仍然(尽管需要做更多工作)能够支持“可选”参数。

    问题的过程foo在F2008下不是C-interoperable(即使是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 下,我们会得到结果,即使用不存在的参数引用此过程表示将空指针传递给过程。

    在 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) 并支持可选参数,我将首先创建一个带有 type(c_ptr), value, intent(in) :: val 的子例程 foo_f(val) bind(c, name='foo_c')。然后包装子例程为foo_f_wrap(val)integer, intent(in), target, optional :: val,如果val 存在,则调用fun_f(c_loc(val)),如果不存在,则调用fun_f(c_null_ptr)。我做对了吗?
    • 我添加了示例,每个环绕方向一个。
    • 完美。我认为这对于任何查看这个问题并考虑 C->F 和 F->C 调用方向的人来说确实很清楚。
    猜你喜欢
    • 2014-01-14
    • 2012-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-16
    • 2016-10-13
    相关资源
    最近更新 更多