Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrapping a pre-initialized pointer in a cython class

I'm trying to use a C library which uses a callback function (callback_function) to provide a pointer to a struct I'd like to wrap (glp_tree).

What is the correct way to initialize an instance with a pointer not created in __cinit__? I can't find an example of this pattern in the cython documentation.

I have some working code (see below), which casts the pointer to an integer and back, but I'm not sure this is good practice / sane.

cdef extern from "stdint.h":
    ctypedef unsigned long long uint64_t

cdef extern from "glpk.h":
    ctypedef struct glp_tree:
        pass

cdef void callback_func(glp_tree* tree, void *info):
    treeobj = Tree(<uint64_t>tree) // cast to an integer...

cdef class Tree:
    cdef glp_tree* ptr
    def __init__(self, uint64_t ptr):
        self.ptr = <glp_tree*>ptr // ... and back to a pointer

Passing the glp_tree object directly seems to work (although it's not what I want to do), but trying to pass the pointer results in a compiler error:

Cannot convert 'glp_tree *' to Python object
like image 407
Snorfalorpagus Avatar asked Sep 09 '13 09:09

Snorfalorpagus


2 Answers

Instead of using __init__/__cinit__ (which always expects Python objects as arguments), you can use a custom @staticmethod cdef to create instances:

cdef class Tree:
    cdef glp_tree* ptr

    def __init__(self, *args):
        raise TypeError('Cannot create instance from Python')

    @staticmethod
    cdef Tree create(glp_tree* ptr):
        obj = <Tree>Tree.__new__(Tree) # create instance without calling __init__
        obj.ptr = ptr
        return obj
like image 167
Daniel Avatar answered Nov 10 '22 22:11

Daniel


Casting a pointer to an integer is an option, but then the correct type to use is uintptr_t, not uint64_t (it's self-documenting and always has the right width for the platform).

The problem is that constructing a Tree is a Python operation, as you can clearly see in the cython -a output. The input to the constructor has to be converted to Python data structures, and pointers have no obvious conversion.

like image 2
Fred Foo Avatar answered Nov 10 '22 23:11

Fred Foo