Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>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. </p> <p>The implementation is show below, where I've also defined convenience functions to be used inside fortran to allocate, deallocate and initialize the nodes.</p> <pre><code>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 =&gt; 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 &gt; 1) then node%next =&gt; 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 </code></pre> <p>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.</p> <pre><code>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 =&gt; 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 </code></pre> <p>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.</p> <p>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. </p> <pre><code>gfortran -fPIC -shared -o libnode.so node.f90 </code></pre> <p>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.</p> <pre><code>#!/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() </code></pre> <p>Executing this gives me the following output:</p> <pre><code>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 </code></pre> <p>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.</p> <p>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.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload