Calling Fortran subroutines with optional arguments from C++

前端 未结 2 652
醉酒成梦
醉酒成梦 2020-12-10 06:11

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

相关标签:
2条回答
  • 2020-12-10 06:27

    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.

    0 讨论(0)
  • 2020-12-10 06:36

    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.

    0 讨论(0)
提交回复
热议问题