M.E.
M.E.

Reputation: 5505

How do you free memory allocated in a function invoked via iso_c_binding and then passed to python via f2py?

I have the following test library written in C, one function allocates an array and the other one is intended to free that memory. Note that the returned array is passed as pointer to pointer as in the real application I will need functions returning several arrays:

#include <stdlib.h>
#include <stdio.h>
void return_array(int length, int **myarray);
void free_array(void *myarray);

void return_array(int length, int **myarray) {
        int i;
        *myarray =  malloc(length*sizeof(int));
        for(i=0; i<length; i++) {
                (*myarray)[i] = i+1;
        }
}

void free_array(void *myarray) {
        free(myarray);
}

And the corresponding wrapper which uses iso_c_binding and f2py:

module test_c_lib

        use iso_c_binding
        implicit none
    
contains

    subroutine return_array(l, z)

            use iso_c_binding

            integer, intent(in) :: l
            integer, intent(out) :: z(l)

            type(c_ptr) :: ret_c_ptr
            integer, pointer :: f_ptr(:)

            interface
                    subroutine c_return_array(c_l, c_z) bind(C, name="return_array")
                            import
                            integer(c_int),value :: c_l
                            type(c_ptr) :: c_z(*)
                    end subroutine
            end interface

            call c_return_array(10, ret_c_ptr)
            call c_f_pointer(ret_c_ptr, f_ptr, [l])
            z = f_ptr
    end subroutine

    subroutine free_integer_array(z)

            use iso_c_binding

            integer, pointer :: z(:)
            type(c_ptr) :: c_z

            interface
                    subroutine c_free_array(c_array) bind (C, name="free_array")
                            import
                            type(c_ptr) :: c_array(*)
                    end subroutine
            end interface

            c_z = loc(z)
            call c_free_array(c_z)

    end subroutine
end module

And the corresponding makefile:

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

But I get:

  xxx |                 c_z = loc(z)
      |                      1
Error: Cannot convert INTEGER(8) to TYPE(c_ptr) at (1)

There might be more issues or bad practices in the way both routines are constructed, but the above code is what I have tried.

I am using gfortran in FreeBSD12.

EDIT

Following Vladimir´s answer I recode free_integer_array incorporating two changes:

    subroutine free_integer_array(z)

            use iso_c_binding

            integer, pointer :: z
            type(c_ptr) :: c_z

            interface
                    subroutine c_free_array(c_array) bind (C, name="free_array")
                            import
                            type(c_ptr) :: c_array(*)
                    end subroutine
            end interface

            c_z = c_loc(z)
            call c_free_array(c_z)

    end subroutine

Then I consume the library generated by f2py as follows:

>>> import f_mod
>>> a = f_mod.test_c_lib.return_array(10)
>>> a
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10], dtype=int32)

So far so good, a numpy array is returned. Then I want to free it:

>>> free_integer_array(a)

And here kernel dies after a while, no other info in the Jupyter Notebook log other than [Errno 79] Inappropriate file type or format, but not even sure that is related to Jupyter Notebook or to the actual program.

Upvotes: 0

Views: 157

Answers (1)

To get a type(c_ptr) pointer one uses c_loc(). I stead, the loc() function is to get an integer containg the addres - usable for Cray (integer) pointers.

Upvotes: 1

Related Questions