I have a large Fortran code base for which I am trying to write a Python interface. I decided I would go the Cython route. However, I have derived types that are not C-interoperable. As an example,
type :: SomeType
real, allocatable :: not_interoperable
[other members]
contains
procedure :: init
end type
...
subroutine init(this) bind(c)
class(SomeType), intent(inout) :: this
...
end subroutine init
I would be completely happy not being able to access the member not_interoperable
from Python, and would actually prefer not to expose it to the user anyway. i.e. some python class that includes only the other members would be perfectly fine, as long as functions inside Fortran can still "see" them. Here, the compiler fails because init
has a polymorphic dummy argument. I can remove init
as a type-bound procedure, and then make the change class(SomeType)
-> type(SomeType)
, but then I still get the error that this
is a dummy argument to a bind(c) procedure but is not C interoperable because the derived type is not C interoperable.
Is there any way around this? The only solution I can see is removing all non-C-interoperable members out of SomeType
and making them global/module variables in the Fortran code, but I'd rather avoid that. Basically, I want a way to hide variables from Python (like those that are not C interoperable) while still keeping the Fortran code modular/preventing global variables.
Some more details:
My program roughly works as:
type(SomeType) :: instance
instance%init() ! no input arguments, but reads a file
instance%do_some_stuff()
instance%do_stuff_with_args(...) ! args are all C-interoperable
instance%finalize()
I would like a similar workflow in Python
from FortranModule import SomeType
instance = SomeType(...) # does not read a file, variables passed directly
instance.do_stuff()
instance.member = 3
instance.do_stuff_with_args(...)
instance.finalize()
But in Python, not_interoperable
is never accessed. I want my Fortran code to still have it and use it, and it is still affected by SomeType%init
, but I do not want/need it in Python. In principle I guess I can hide it as a global variable which gets modified by this function, but then (a) I can only have one instance and (b) I don't like global variables as a general rule (general consensus is that it reduces modularity and maintainability).
Also, just for clarity: I have a lot of non-interoperable types, and they are not as simple as real, allocatable
, so rewriting all of them is not really realistic.
AFAIK there's no way to interoperate with C a derived type containing allocatable component). A solution is to expose to C only an opaque handle (void* C pointer) to a derived type instance. Then there's the choice to keep the components completely hidden from C, or to give C some pointers to them.
Illustration code:
Fortran part:
! ===========================================
! file: FC_bind_handle_f.f90
module somemodule
use, intrinsic :: iso_c_binding
implicit none
! c_float is needed only if one wants to
! access %a directly from C
type :: sometype
integer(c_int), allocatable :: a(:)
end type
contains
! example routine that processes the content of the type
subroutine sometype_x2_f(x)
type(sometype), intent(inout) :: x
x%a = 2 * x%a
end subroutine
end module
! ===========================================
! ===========================================
! file: somemodule_wrap.f90
module somemodule_wrap
use, intrinsic :: iso_c_binding
use somemodule
implicit none
contains
! function that creates a sometype instance,
! allocates the %a component,
! and returns a C pointer to the instance
type(c_ptr) function sometype_create(len) bind(c)
integer(c_int), value :: len
type(sometype), pointer :: p
allocate(p)
allocate(p%a(len))
sometype_create = c_loc(p)
end function
! routine that releases a handle
subroutine sometype_free(ptr) bind(c)
type(c_ptr) :: ptr
type(sometype), pointer :: p
call c_f_pointer(ptr,p)
deallocate(p%a) ! would actually be automatically deallocated
deallocate(p)
ptr = c_null_ptr
end subroutine
! function that returns a C pointer to the %a component,
! allowing to work directly on it from C
type(c_ptr) function sometype_a_ptr(ptr, n) bind(c)
type(c_ptr), value :: ptr
integer(c_int), intent(out) :: n
type(sometype), pointer :: p
call c_f_pointer(ptr,p)
n = size(p%a)
sometype_a_ptr = c_loc(p%a)
end function
! wrapper routine to a Fortran routine that performs
! some processing on a sometype instance
subroutine sometype_x2(ptr) bind(c)
type(c_ptr), value :: ptr
type(sometype), pointer :: p
call c_f_pointer(ptr,p)
call sometype_x2_f(p)
end subroutine
end module
! ===========================================
C part:
//===========================================
//file: FC_bind_handle_c.c
#include <stdio.h>
void* sometype_create(int len);
void* sometype_free(void** h);
int* sometype_a_ptr(void* h, int* n);
void* sometype_x2(void* h);
int main(void)
{
// creates an instance of sometype with 6 elements in %a
void* h = sometype_create( 6 );
// get a pointer to %a and fill it. Alternatively one could
// define a "put" routine to avoid any access to %a from C
int n;
int* a = sometype_a_ptr(h, &n);
for (int i = 0; i < n; i++) {
a[i] = i;
}
// print the content of the instance
for (int i = 0; i < n; i++) printf("%d ",a[i]);
printf("\n\n");
// double all the values
sometype_x2(h);
// print the content of the instance
for (int i = 0; i < n; i++) printf("%d ",a[i]);
printf("\n\n");
// releases the instance
sometype_free(&h);
return 0;
}
//===========================================
Result:
$ gfortran -c FC_bind_handle_f.f90 && gcc FC_bind_handle_c.c FC_bind_handle_f.o -lgfortran
$ ./a.out
0 1 2 3 4 5
0 2 4 6 8 10
However, managing C pointers from Python is maybe not convenient/possible (I don't know enough of python...). Then, instead of returning void* pointer handle, one can return identifiers, which refer to instances in a global array:
! ===========================================
! file: FC_bind_id_f.f90
module somemodule
use, intrinsic :: iso_c_binding
implicit none
! c_float is needed only if one wants to
! access %a directly from C
type :: sometype
integer(c_int), allocatable :: a(:)
end type
contains
! example routine that processes the content of the type
subroutine sometype_x2_f(x)
type(sometype), intent(inout) :: x
x%a = 2 * x%a
end subroutine
end module
! ===========================================
! ===========================================
! file: somemodule_wrap.f90
module somemodule_wrap
use, intrinsic :: iso_c_binding
use somemodule
implicit none
! again, "target" is needed only if one wants to access the %a component from C
type(sometype), allocatable, target :: h(:)
logical, allocatable :: active(:)
contains
! function that creates a sometype instance,
! allocates the %a component,
! and returns a C pointer to the instance
integer(c_int) function sometype_create(len) bind(c)
integer(c_int), value :: len
integer :: id
if (.not.allocated(h)) then
allocate(h(0))
allocate(active(0))
end if
id = findloc(active,.false.,dim=1)
if (id == 0) then
h = [h, sometype()]
active = [active, .false.]
id = size(h)
end if
active(id) = .true.
allocate(h(id)%a(len))
sometype_create = id
end function
! routine that releases a handle
subroutine sometype_free(id) bind(c)
integer(c_int) :: id
deallocate(h(id)%a)
active(id) = .false.
if (id == size(h)) then
do while (id > 0 .and. .not.active(id))
h = h(1:id-1)
active = active(1:id-1)
id = id-1
end do
end if
id = 0
end subroutine
! function that returns a C pointer to the %a component,
! allowing to work directly on it from C
type(c_ptr) function sometype_a_ptr(id, n) bind(c)
integer(c_int), value :: id
integer(c_int), intent(out) :: n
n = size(h(id)%a)
sometype_a_ptr = c_loc(h(id)%a)
end function
! wrapper routine to a Fortran routine that performs
! some processing on a sometype instance
subroutine sometype_x2(id) bind(c)
integer(c_int), value :: id
call sometype_x2_f(h(id))
end subroutine
end module
! ===========================================
C part:
//===========================================
//file: FC_bind_id_c.c
#include <stdio.h>
int sometype_create(int len);
void* sometype_free(int* id);
int* sometype_a_ptr(int id, int* n);
void* sometype_x2(int id);
int main(void)
{
// creates an instance of sometype with 6 elements in %a
int id = sometype_create( 6 );
// get a pointer to %a and fill it. Alternatively one could
// define a "put" routine to avoid any access to %a from C
int n;
int* a = sometype_a_ptr(id, &n);
for (int i = 0; i < n; i++) {
a[i] = i;
}
// print the content of the instance
for (int i = 0; i < n; i++) printf("%d ",a[i]);
printf("\n\n");
// double all the values
sometype_x2(id);
// print the content of the instance
for (int i = 0; i < n; i++) printf("%d ",a[i]);
printf("\n\n");
// releases the instance
sometype_free(&id);
return 0;
}
//===========================================