fortranfortran-iso-c-bindingderived-types

How do I convert an array of structs into an array of derived type with ISO C BINDINGS?


I have the following test C library:

#include <stdlib.h>

struct mystruct {
    int a;
    double  b;
};

struct mystruct *return_array_of_structs(int *size);

struct mystruct *return_array_of_structs(int *size) {

    int i;
    struct mystruct *ptr;
    ptr = malloc(sizeof(struct mystruct)*10);
    
    for(i=0; i<10; i++) {
        ptr[i].a = i+1;
        ptr[i].b = (i+1)*1.0L;
    }
    
    *size=10;
    return ptr;
}

And the following module intended to be compiled with f2py:

module test_c_lib

        use iso_c_binding
        implicit none

        type :: t_mystruct
            integer :: a
            real(8) :: b
        end type
        
contains

        subroutine test()

                use iso_c_binding
                
                type(c_ptr)     :: ret_c_ptr            
                integer         :: length
                
                ! Interface to C function
                interface                        
                        type(c_ptr) function c_return_array_of_structs(a) bind(C, name="return_array_of_structs")
                                import
                                integer(c_int) :: a
                        end function
                end interface

                ! Call C function               
                ret_c_ptr = c_return_array_of_structs(length)

        
        end subroutine

end module

The following makefile compiles this:

f_mod.so:       f_mod.f90 c_lib.o
                f2py -c f_mod.f90 c_lib.o -m f_mod

c_lib.o:        c_lib.c
                gcc -c -fpic c_lib.c -o c_lib.o

I can load the library in Python normally and execute the test() subroutine without problems.

import f_mod
f_mod.test_c_lib.test()

But I do not how to convert ret_c_ptr into an array of derived type t_mystruct that I can normally operate as an array in Fortran. Any hint? Note: the question is related to iso c bindings and Fortran, not to f2py or its integration with Python.


Solution

  • If you have a C pointer and want to associate a Fortran pointer with the target of that C pointer, you want to use c_f_pointer to do that association. c_f_pointer doesn't (generally) care whether the type is an intrinsic one or not, or scalar or not.

    Let's define the Fortran derived type as an interoperable1 one:

    type, bind(c) :: t_mystruct
      integer(c_int) :: a
      real(c_double) :: b
    end type
    

    and then have a Fortran pointer array of that type:

    type(t_mystruct), pointer :: ptr_f(:)
    

    With ptr_c pointing to a suitable lump of memory we can do the association:

    call c_f_pointer(ptr_c, ptr_f, [length])
    

    to make ptr_f an array of shape [length] pointing to the C target.


    1 Being interoperable isn't necessary, but it's certainly helpful to be if it can be.