I am wondering what is the correct way of interfacing with C, when the C methods have optional arguments (i.e. you are allowed to pass NULL) and you want the optional arguments to propage to the Fortran API.
- Is there a benefit in including the
optionalkeyword in both the Fortran method's argument list and the interface block? see:err_cargument innull_return_f90>C_API. - Is it legal to initialise a local variable with an optional input variable and pass that to the C interface? (this feels wrong, it doesn't feel like it should work, yet it does). see:
name_cvariable, its initialisation and input toC_APIinnull_str_f90subroutine.
I have provided a MWE below demonstrating what I am asking and as far as I can tell, the answers to my questions are 1. it doesn't matter 2. yes, but I am not entirely convinced they are correct, especially 2.
If there is a better way on how to write the Fortran API I would be open to that as well, but what I cannot do is edit the C API.
Related post: Calling Fortran subroutines with optional arguments from C++
MWE
program main
use, intrinsic :: iso_c_binding
implicit none
integer(c_int) :: err
call null_str_f90("abc")
call null_str_f90()
print*, repeat("*", 10)
call null_return_f90()
call null_return_f90(err)
print*, "error code:", err
contains
function istring_(o) result(v)
character(len=*), intent(in) :: o
character(len=:, kind=c_char), allocatable :: v
v = trim(o)//c_null_char
end function istring_
subroutine null_return_f90(err)
interface
subroutine C_API(err_c) bind(C, name="null_return")
use, intrinsic :: iso_c_binding
integer(c_int), optional, intent(out) :: err_c ! 1. does the optional do anything?
end subroutine C_API
end interface
integer(c_int), optional, intent(out) :: err
call C_API(err_c=err) ! 1. Is this safe & portable?
end subroutine null_return_f90
subroutine null_str_f90(str)
interface
subroutine C_API(str_c) bind(C, name="null_str")
use, intrinsic :: iso_c_binding
character(len=1, kind=c_char), dimension(*), optional, intent(in) :: str_c
end subroutine C_API
end interface
character(len=*), intent(in), optional :: str
! Local variables
character(len=:, kind=c_char), allocatable :: name_c
if (present(str)) name_c = istring_(str)
call C_API(str_c=name_c) ! 2. Is this safe & portable?
end subroutine null_str_f90
end program main
#include <stdio.h>
void null_str(const char *str) {
if (str) {
printf("str is present: str is %s\n", str);
} else {
printf("str is not present\n");
}
}
void null_return(int *opt) {
if (opt) {
*opt = 1;
} else {
printf("opt is not present\n");
}
}
Compiling
Turn on aggressive non-IEEE compliant optimisations to ensure this will still work.
gcc -c -Ofast -Wall null_args.c -o null_args.o
gfortran -Ofast -Wall null_args.o null_args.f90 -o null_args
./null_args
Output
str is present: str is abc
str is not present
**********
opt is not present
error code: 1
**********
For
null_return_f90and itsC_API, we must haveerr_cin that latter as an optional argument. Without it being optional, we cannot associate an absenterrwith it in that call. If it is optional, then it's fine to use the optional and not present actual argumenterr.Looking at the subroutine
null_str_f90we have the following:Your concern is that if the actual argument
stris not present, thenname_cis undefined1 when it comes to the call toC_API. This is true, but is not a concern.name_cremains not allocated rather than simply undefined. An allocatable actual argument which is not allocated (or a pointer which is not associated) is allowed to be associated with an ordinary optional dummy argument. In this case it will be treated as a not present actual argument, just as if it weren't given at all.1 You state "uninitialized" but definition and initialization are very different things, and initialization is not relevant to this case.