Calling Fortran subroutines with optional arguments from C++
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.
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")
use iso_c_binding, only: c_float
real(c_float), intent(in), optional :: a, b, c
...
end subroutine foo
(for greater portability I used real(c_float)
from the iso_c_binding
module, 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 and was later incorporated into Fortran 2018.