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 declared struct 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 *
and struct 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.
MODULE Fortran_Wrapper
USE, INTRINSIC :: ISO_C_BINDING, ONLY: xxxxx
...
! Enum definition in here somewhere.
...
PUBLIC :: foobar
PUBLIC :: Create
! Wrapper for a pointer to foobar_s.
TYPE :: foobar
PRIVATE
TYPE(C_PTR) :: ptr = C_NULL_PTR
CONTAINS
FINAL :: final
END TYPE foobar
...
CONTAINS
! Wrapper around foo_create, exposed to Fortran client code.
FUNCTION Create(mode, n) RESULT(obj)
INTEGER(KIND(ENUM_FOO_CONSTANT)), INTENT(IN) :: mode
! Perhaps the next argument is taken as default integer, and
! you do kind conversion inside this wrapper.
INTEGER(C_INT), INTENT(IN) :: n
TYPE(foobar) :: obj
INTERFACE
FUNCTION foo_create(mode, n) BIND(C, NAME='foo_create')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_INT, C_PTR
IMPORT :: ENUM_FOO_CONSTANT
IMPLICIT NONE
INTEGER(KIND(ENUM_FOO_CONSTANT)), VALUE :: mode
INTEGER(KIND=C_INT), VALUE :: n
TYPE(C_PTR) :: foo_create
END FUNCTION foo_create
END INTERFACE
obj%ptr = foo_create(mode, n)
END FUNCTION Create
! Use a finalizer to do automatic cleanup off the C structures.
! (Impure elemental is F2008.)
IMPURE ELEMENTAL SUBROUTINE final(obj)
TYPE(foobar), INTENT(INOUT) :: obj
INTERFACE
SUBROUTINE foo_destroy(obj) BIND(C, NAME='foo_destroy')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
IMPLICIT NONE
TYPE(C_PTR), VALUE :: obj
END SUBROUTINE foo_destroy
END INTERFACE
IF (C_ASSOCIATED(obj%ptr)) CALL foo_destroy(obj%ptr)
END SUBROUTINE final
END MODULE Fortran_Wrapper
(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*)
.)