As has Bálint Aradi already mentioned in the comments, the node is not interoperable with C in it's current form. For that you need to change fortran pointers into C pointers, but that makes it extremely painful to use inside fortran itself. The most elegant solution I could come up with was putting the C interoperable type inside a fortran type and holding separate versions of C and fortran pointers.
The implementation is show below, where I've also defined convenience functions to be used inside fortran to allocate, deallocate and initialize the nodes.
module node_mod
use, intrinsic :: iso_c_binding
implicit none
! the C interoperable type
type, bind(c) :: cnode
type(c_ptr) :: self = c_null_ptr
type(c_ptr) :: next = c_null_ptr
integer(c_int) :: value
end type cnode
! the type used for work in fortran
type :: fnode
type(cnode) :: c
type(fnode), pointer :: next => null()
end type fnode
contains
recursive function allocate_nodes(n, v) result(node)
integer, intent(in) :: n
integer, optional, intent(in) :: v
type(fnode), pointer :: node
integer :: val
allocate(node)
if (present(v)) then
val = v
else
val = 1
end if
node%c%value = val
if (n > 1) then
node%next => allocate_nodes(n-1, val+1)
end if
end function allocate_nodes
recursive subroutine deallocate_nodes(node)
type(fnode), pointer, intent(inout) :: node
if (associated(node%next)) then
call deallocate_nodes(node%next)
end if
deallocate(node)
end subroutine deallocate_nodes
end module node_mod
As you can see, there's an extra "%c" needed to access the "value" element, which is a bit of a nuisance. To use the previously defined routines inside python to retrieve the linked list, C interoperable wrappers must be defined and C pointers must be linked.
module node_mod_cinter
use, intrinsic :: iso_c_binding
use, non_intrinsic :: node_mod
implicit none
contains
recursive subroutine set_cptr(node)
type(fnode), pointer, intent(in) :: node
node%c%self = c_loc(node)
if (associated(node%next)) then
node%c%next = c_loc(node%next%c)
call set_cptr(node%next)
end if
end subroutine set_cptr
function allocate_nodes_citer(n) bind(c, name="allocate_nodes") result(cptr)
integer(c_int), value, intent(in) :: n
type(c_ptr) :: cptr
type(fnode), pointer :: node
node => allocate_nodes(n)
call set_cptr(node)
cptr = c_loc(node%c)
end function allocate_nodes_citer
subroutine deallocate_nodes_citer(cptr) bind(c, name="deallocate_nodes")
type(c_ptr), value, intent(in) :: cptr
type(cnode), pointer :: subnode
type(fnode), pointer :: node
call c_f_pointer(cptr, subnode)
call c_f_pointer(subnode%self, node)
call deallocate_nodes(node)
end subroutine deallocate_nodes_citer
end module node_mod_cinter
The "*_nodes_citer" routines simply deal with the different pointer types and the set_cptr subroutine links the C pointers inside the C interoperable type according to the fortran pointers. I've added the node%c%self element so that the fortran pointer could be recovered and used for proper deallocation, but if you're not too much concerned about that, then its not strictly needed.
This code needs to be compiled as a shared library to be used by other programs. I used the following command with gfortran on my linux box.
gfortran -fPIC -shared -o libnode.so node.f90
Finally, the python code to allocate a list of 10 nodes, print out the node%c%value of each of them and then deallocate everything again. Additionally, the memory locations of fortran and C nodes are also shown.
#!/usr/bin/env python
import ctypes
from ctypes import POINTER, c_int, c_void_p
class Node(ctypes.Structure):
pass
Node._fields_ = (
("self", c_void_p),
("next", POINTER(Node)),
("value", c_int),
)
def define_function(res, args, paramflags, name, lib):
prot = ctypes.CFUNCTYPE(res, *args)
return prot((name, lib), paramflags)
def main():
import os.path
libpath = os.path.abspath("libnode.so")
lib = ctypes.cdll.LoadLibrary(libpath)
allocate_nodes = define_function(
res=POINTER(Node),
args=(
c_int,
),
paramflags=(
(1, "n"),
),
name="allocate_nodes",
lib=lib,
)
deallocate_nodes = define_function(
res=None,
args=(
POINTER(Node),
),
paramflags=(
(1, "cptr"),
),
name="deallocate_nodes",
lib=lib,
)
node_ptr = allocate_nodes(10)
n = node_ptr[0]
print "value", "f_ptr", "c_ptr"
while True:
print n.value, n.self, ctypes.addressof(n)
if n.next:
n = n.next[0]
else:
break
deallocate_nodes(node_ptr)
if __name__ == "__main__":
main()
Executing this gives me the following output:
value f_ptr c_ptr
1 15356144 15356144
2 15220144 15220144
3 15320384 15320384
4 14700384 14700384
5 15661152 15661152
6 15661200 15661200
7 15661248 15661248
8 14886672 14886672
9 14886720 14886720
10 14886768 14886768
It's interesting to note that both node types start at the same memory locations, so node%c%self was not really needed, but this is only because I was careful with the type definitions and this really should not be counted on.
And there you have it. Even without having to deal with linked lists it's quite a hassle, but ctypes are vastly more powerful and robust that f2py. Hope some good comes out of this.