Calling Fortran subroutines with optional arguments from C++

妖精的绣舞 提交于 2019-11-27 03:26:01

问题


How would I reference a Fortran function in a C++ header that uses optional arguments? Would I have a prototype in the header for each possible combination of calls? Or is this even possible?

For instance, Fortran:

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

回答1:


It is not possible, at least portably, unless you make the subroutine bind(C).

Once you make it bind(C), it is just passing of a pointer which can be NULL on the C side.

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

(for greater portability real(c_float) from the iso_c_binding module should be used, but that is somewhat tangential to this question)

In C(++)

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

foo(&local_a, NULL, NULL);

and then you can make a C++ function which calls foo and which employs C++-style optional parameters.

This capability was allowed in Fortran in Technical Specification ISO/IEC TS 29113:2012 on further interoperability of Fortran with C.




回答2:


As Vladimir F answers, under Fortran 2018 (and Fortran 2008+TS29113) it is possible to use the optional attribute for dummy arguments in a C interoperable Fortran procedure.

Under Fortran 2008 that is not possible.  Several compilers still currently do not support this feature.  With these compilers one is still (albeit with more work) able to support "optional" arguments.

The procedure foo of the question is not C-interoperable under F2008 (even with bind(C)).  However, it is possible under F2008 to mimic this idea: have a C-interoperable procedure with type(c_ptr) arguments which wraps the desired Fortran procedure.  This interoperable wrapper can check for null pointers (using C_ASSOCIATED) to determine whether onward passed arguments are present or not - and pass the dereferenced arguments if so.

For example, the Fortran side with a C-interoperable wrapper may look like

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

Under Fortran 2018 we have this symmetry in the interoperable interface: if the procedure is defined by means other than Fortran but the interoperable interface has an optional argument, then under F2018 we have the result that referencing this procedure with the argument not present means that the null pointer is passed to the procedure.

Under F2008 we need to handle that side too: we again do that with a F2008 non-interoperable procedure which wraps an interoperable procedure with type(c_ptr) arguments: if the argument is present, pass its address; if not, pass C_NULL_PTR.

Such F2008 code may look like

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

Be aware of limitations here caused by the use of c_loc: for certain cases one may want to use a copy or take other protective measures.



来源:https://stackoverflow.com/questions/40089567/calling-fortran-subroutines-with-optional-arguments-from-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!