Reputation: 87
I have a commercial C library which I want to use with Fortran. There are two functions and a pointer struct like:
struct env;
typedef struct env *ENVptr;
the two functions have the prototype:
ENVptr open(int *status_p);
int close(ENVptr **env_p);
I wrote an Fortran interface to access them:
interface
function c_open(status) bind(c, name='c_open')
use, intrinsic :: iso_c_binding
integer(kind = c_int) :: status
type(c_ptr) :: c_open
end function
function c_close(env) bind(c, name='c_close')
use, intrinsic :: iso_c_binding
type(c_ptr) :: env
integer(kind = c_int) :: c_close
end function
end interface
I use this code to use this in my Fortran program:
type(c_ptr) :: env = c_null_ptr
env = c_open(status)
if ( status .ne. 0 ) then
print *, 'Could not open environment'
stop
end if
...some more code...
if ( c_associated(env) ) then
status = c_close(env)
if ( status .ne. 0 ) then
print *, 'Could not close environment.'
end if
end if
but when I execute the program, I get an Segmentation fault error when the program reaches the c_close
function.
Is this the right way to interface the C routines?
Upvotes: 1
Views: 797
Reputation: 974
I don't see how your program could have linked because the binding name of a procedure has to agree with the name in the actual C prototype. I suppose you could square the names up with a *.def
file. Also Fortran has a concept of argument keywords so it is good practice in my opinion to make the Fortran dummy arguments in the interface agree with their documented argument names. Other than that you seem to have the right level of indirection in your interface bodies, so my version would be:
interface
function c_open(status_p) bind(C,name='open')
use, intrinsic :: iso_c_binding
implicit none
type(c_ptr) :: c_open
integer(kind = c_int) status_p
end function c_open
function c_close(env_p) bind(c,name='close')
use, intrinsic :: iso_c_binding
implicit none
integer(c_int) c_close
type(c_ptr) env_p
end function c_close
end interface
Now, there is a problem with the level of indirection when you invoke c_close
because the C typedef for ENVptr
already makes it a pointer, so ENVptr** envp
is a pointer to a pointer to a pointer. In your Fortran code you are passing a c_ptr
that points at an opaque type by reference, so you are passing a pointer to a pointer. Thus you need to create an extra level of indirection to make it fly. I would accordingly attempt to modify your code to something like:
type(c_ptr) :: env = c_null_ptr, envpp = c_null_ptr
target env
integer(c_int) status
env = c_open(status)
if ( status .ne. 0 ) then
print *, 'Could not open environment'
stop
end if
!...some more code...
if ( c_associated(env) ) then
envpp = c_loc(env)
status = c_close(envpp)
if ( status .ne. 0 ) then
print *, 'Could not close environment.'
end if
end if
I can't test this obviously but at this point it's syntactically correct and has the right level of indirection according to my reading of your problem statement.
Upvotes: 1