How to use malloc and free with python ctypes?

Question:

I have a function in my C library, say runsim() which takes pointer to struct repdata as one of the arguments, where struct repdata is given by

struct repdata {
    int *var1;
    int *var2;
    int *var3;
    char *var4;
    double *var5;
    double *var6;
    int *var7;
};

When using C exclusively, I initialize a variable of type struct repdata calling the function,

struct repdata data;
void create_data_container(struct repdata *data, int len_data)
{

    data -> var1 = malloc( sizeof(int) * len_data );
    data -> var2 = malloc( sizeof(int) * len_data );
    data -> var3 = malloc( sizeof(int) * len_data );
    data -> var4 = malloc( sizeof(char) * len_data );
    data -> var5 = malloc( sizeof(double) * len_data);
    data -> var6 = malloc( sizeof(double) * len_data);
    data -> var7 = malloc( sizeof(int) * len_data);
}

and then fill this struct as simulation proceeds. After I have written the data to a file, I free the memory using the standard

free(data.var1);
free(data.var2);
.
.
.
free(data.var7);

I want to call the runsim() function from Python using Python Ctypes. For this I need to pass a pointer to a variable (that is equivalent to the type of struct repdata) as one of the runsim() arguments. Suppose that in Python I have defined an equivalent of struct repdata in the following manner.

import ctypes as C

class Repdata(C.Structure):
    _fields_ = [
        ("var1", C.POINTER(C.c_int)),
        ("var2", C.POINTER(C.c_int)),
        ("var3", C.POINTER(C.c_int)),
        ("var4", C.POINTER(C.c_char)),
        ("var5", C.POINTER(C.c_double)),
        ("var6", C.POINTER(C.c_double)),
        ("var7", C.POINTER(C.c_int)),
    ]

What is the equivalent of create_data_container function shown above on the Python side? I want to initialize an instance of Repdata which can be passed to C code and has sufficient memory for storing the replication data. And, once simulation is completed, how do I free the memory from Python?

I am using Ubuntu Linux 12.04.

Thanks in advance for your help.

Asked By: Curious2learn

||

Answers:

You can allocate buffers using ctypes and assign them to the pointers. Once the Python ctypes objects have no references they will be freed automatically. Here’s a simple example (with a Windows DLL…don’t have a Linux machine handy, but the idea is the same) and a Python wrapper.

create_string_buffer allocates a writable buffer that can be passed from Python to C that ctypes will marshal as a char*.

You can also create writable arrays of ctypes types with the syntax:

variable_name = (ctypes_type * length)(initial_values)

test.c

#include <stdio.h>
#include <string.h>

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

typedef struct example {
    char* data;
    int len;          // of data buffer
    double* doubles;
    int count;        // of doubles
} example;

API void func(example* p) {
    strcpy_s(p->data, p->len, "hello, world!");
    for(int i = 0; i < p->count; i++)
        p->doubles[i] = 1.1 * (i + 1);
}

test.py

import ctypes as ct

class Example(ct.Structure):

    _fields_ = (('data', ct.POINTER(ct.c_char)),
                ('len', ct.c_int),
                ('doubles', ct.POINTER(ct.c_double)),
                ('count', ct.c_int))

    def __init__(self, length, count):
        self.data = ct.create_string_buffer(length)
        self.len = length
        self.doubles = (ct.c_double * count)()
        self.count = count

    def __repr__(self):
        return (f'Example({ct.string_at(self.data)}, '
                f'[{", ".join(str(self.doubles[i]) for i in range(self.count))}])')

class Dll:

    def __init__(self):
        self.dll = ct.CDLL('./test')
        self.dll.func.argtypes = ct.POINTER(Example),
        self.dll.func.restype = None

    def func(self, ex):
        self.dll.func(ct.byref(ex))

d = Dll()
e = Example(20, 5)
print('before:', e)
d.func(e)
print('after:', e)

Output:

before: Example(b'', [0.0, 0.0, 0.0, 0.0, 0.0])
after: Example(b'hello, world!', [1.1, 2.2, 3.3000000000000003, 4.4, 5.5])
Answered By: Mark Tolonen
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.