Search code examples
pythonclistpointerspython-cffi

Pass a list of object references to a CFFI function


I am coding on a project where I need to process many tree-like data elements, to overcome some performance issues I want to call a given processing function already written in C with the signature int process(Node* root) and a tree node defined as:

typedef struct Node {
    int value;
    struct Node ** children;
    int num_children;
} Node;

Thus a tree node can have an arbitrary number of child nodes. Analogously a tree node in my Python project is defined as:

class Node():
    def __init__(self,value):
        self.value = value
        self.children = list()

    def add_child(self,child):
        self.children.append(child)

To call the C function I would like to use CFFI. The problem I ran into is that I need to convert my Python tree data structure to a matching C representation. A node representation, which can be passed to the CFFI function can be created via:

ffi_node = ffi.new("Node *")
ffi_node.value = some_value

But I could not find a way to convert the list references to children to the struct Node** children.


Solution

  • The type struct node ** in C is almost equivalent to struct node *[], which stands for "an array of pointers to nodes". So you want array = ffi.new("struct node *[]", x). This allocates an array of length x, if it is an integer; or, you can directly put in x a list of the cffi pointers, e.g. array = ffi.new("struct node *[]", [child.as_cffi_pointer() for child in self.children]).

    Be careful that all cffi objects created with ffi.new() must be kept alive manually as long as needed. Just storing the C pointer somewhere will not keep alive the cffi object in Python. If you forget to keep alive the Python-side cffi object, then the C pointer will quickly point to deallocated memory. This concerns both the ffi.new("struct node *") and the ffi.new("struct node *[]", x).