But at the time I enter the C code, *ierr holds random garbage (expecting -1). And even changing it inside bsaclInit(), then exiting bsacl_Init(), ierr still holds -1, i.e. did not get modified (even though ierr in c-code is).
Shouldn’t c_loc() work normally with scalar fortran variables? It looks like is not working as in the C counterpart bsaclInit(&ierr_);.
I then changed (for both main Fortran program and interafce module) to /iface:stdcall. This led to correct behavior up to bsacl_Init(). Then, returning to the main program right after, ierr returned to hold -1.
cloc(ierr) returns the address of ierr. The Fortran compiler then creates a temporary variable on the stack that contains this address, and then passes the address of the temporary variable. So basically it passes the address of the address of ierr, and on the C side you should have
void bsaclInit(int **ierr)
Alternatively, without changing the C interface, your call should be:
call bsaclInit(ierr)
Note that you are assuming that a Fortran integer maps to a C int. Although true most of time, this is not guaranteed. A more robust code would use the C binding features:
subroutine bsacl_Init(ierr)
use iso_c_binding
integer, intent(inout) :: ierr
interface
subroutine bsaclInit(ierr) bind(C)
integer(c_int) :: ierr
end subroutine
end interface
integer(c_int) :: ierr_c
ierr_c = ierr
call bsaclInit(ierr_c)
ierr = ierr_c
end subroutine
Thanks @PierU , I get it now.
I was assuming the Fortran compiler was passing the value of the temporary c_loc(ierr), instead of its address. Strange because actually in bsacl_init() I was then having the right behavior (after the call, ierr became 0).
It was at the time of exiting, that in the main program ierr returned to hold -1, because at that point, with stdcall, the compiler did a copy (instead of normally passing the address).
It wasn’t clear if you had an explicit interface or for the C routine. I assumed you were using an implicit interface.
Although describing the interface with type(c_ptr), value :: ierr is (I think) equivalent to integer(c_int) :: ierr, I prefer the latter: the compiler can check the type of the actual argument when calling the routine, whereas c_loc() of virtually any object/type will be OK for the compiler. And it makes clear that the C routine is expecting a pointer to an int and not a generic pointer.