I'm learning Fortran 2003. As a training task, I'm trying to make calls from Fortran 2003 to a C library that uses opaque pointers:
struct foobar_s;
typedef struct foobar_s *foobar;
foobar foo_create(enum foo, unsigned int);
void foo_destroy(foobar);
Most advice I found on the internet tells me to describe the foobar
type as type(c_ptr)
, so the following is supposed to work:
!foobar foo_create(enum foo, unsigned int);
function foo_create(mode,n) bind(c) ret(foo)
type(c_ptr) :: foo
integer(kind(ENUM_FOO_CONSTANT)), value :: mode
integer(kind=c_int), value :: n
end function
This declares foo_create
as returning a void*
instead of foobar
= struct foobar_s *
, but it works on modern architectures anyway.
I have been trying to create a distinct Fortran type, closer to the intent of opaque C pointer. The only thing that worked for me was:
type, bind(c) :: foobar
private
type(c_ptr) :: ptr
end type
which corresponds to:
typedef struct {
void * ptr;
} foobar;
on the C side. Now, §6.7.2.1 of C standard guarantees that the address of the beginning of a struct
is the address of the first element (right?) but there may be some padding at the end of it (but on architectures I use there is not, because pointers are self-aligned), so this whole contraption works on my machines:
!foobar foo_create(enum foo, unsigned int);
function foo_create(mode,n) bind(c) ret(foo)
type(foobar) :: foo
integer(kind(ENUM_FOO_CONSTANT)), value :: mode
integer(kind=c_int), value :: n
end function
!void foo_destroy(foobar);
sobroutine foo_destroy(foo) bind(c)
type(foobar), value :: foo
end subroutine
I have verified that Valgrind shows no errors for a program that calls C functions foo_create()
and foo_destroy()
from Fortran using this type definition. Still, it cannot be a definite proof.
Is the assumption that struct { void * ptr }
has the same size and bit-pattern as struct foobar_s *
going to break? Is this the best way to wrap an opaque C pointer (and create a distinct type) in Fortran 2003?
The C language requires that all declarations that refer to the same object or function have compatible type. Given the effective C declarations of your Fortran code, your approach breaks that requirement. A practical outcome of that requirement is that a compiler could use different approaches to return something declared
struct { void * ptr }
, than something declaredstruct foobar_s *
(e.g the aggregate might be returned in an area denominated by a hidden argument passed to the function, a pointer result might be returned in a register). Such a difference in implementation would be catastrophic for your code.TYPE(C_PTR) in Fortran can be used for both
void *
andstruct foobar_s*
, there is an implied requirement on a companion C compiler to a Fortran processor that the same representation method be used for all C object pointer types (see f2003 note 15.9).A typical approach is to write small Fortran wrapper procedures around the C functions, that appropriate set and reference the private C_PTR component. The Fortran type with the C_PTR component does not need to be interoperable. If the Fortran type is not interoperable, you can use modern Fortran features like type extension and finalizers - foo_destroy looks like something that would be useful to call from a finalizer.
(Note that the Fortran interface body in the question is missing the VALUE attribute on the two dummy argument definitions, otherwise the corresponding C prototype is
foobar foo_create(enum foo*, unsigned int*)
.)