Wrapping a pre-initialized pointer in a cython class - python

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

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

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.

This will work
cdef class Tree:
cdef glp_tree* ptr
def __init__(self, long ptr):
self.ptr = <glp_tree*>PyLong_AsVoidPtr(ptr)

Related

Initializing Cython objects with existing C Objects

C++ Model
Say I have the following C++ data structures I wish to expose to Python.
#include <memory>
#include <vector>
struct mystruct
{
int a, b, c, d, e, f, g, h, i, j, k, l, m;
};
typedef std::vector<std::shared_ptr<mystruct>> mystruct_list;
Boost Python
I can wrap these fairly effectively using boost::python with the following code, easily allowing me to use the existing mystruct (copying the shared_ptr) rather than recreating an existing object.
#include "mystruct.h"
#include <boost/python.hpp>
using namespace boost::python;
BOOST_PYTHON_MODULE(example)
{
class_<mystruct, std::shared_ptr<mystruct>>("MyStruct", init<>())
.def_readwrite("a", &mystruct::a);
// add the rest of the member variables
class_<mystruct_list>("MyStructList", init<>())
.def("at", &mystruct_list::at, return_value_policy<copy_const_reference>());
// add the rest of the member functions
}
Cython
In Cython, I have no idea how to extract an item from mystruct_list, without copying the underlying data. I have no idea how I could initialize MyStruct from the existing shared_ptr<mystruct>, without copying all the data over in one of various forms.
from libcpp.memory cimport shared_ptr
from cython.operator cimport dereference
cdef extern from "mystruct.h" nogil:
cdef cppclass mystruct:
int a, b, c, d, e, f, g, h, i, j, k, l, m
ctypedef vector[v] mystruct_list
cdef class MyStruct:
cdef shared_ptr[mystruct] ptr
def __cinit__(MyStruct self):
self.ptr.reset(new mystruct)
property a:
def __get__(MyStruct self):
return dereference(self.ptr).a
def __set__(MyStruct self, int value):
dereference(self.ptr).a = value
cdef class MyStructList:
cdef mystruct_list c
cdef mystruct_list.iterator it
def __cinit__(MyStructList self):
pass
def __getitem__(MyStructList self, int index):
# How do return MyStruct without copying the underlying `mystruct`
pass
I see many possible workarounds, and none of them are very satisfactory:
I could initialize an empty MyStruct, and in Cython assign over the shared_ptr. However, this would result in wasting an initalized struct for absolutely no reason.
MyStruct value
value.ptr = self.c.at(index)
return value
I also could copy the data from the existing mystruct to the new mystruct. However, this suffers from similar bloat.
MyStruct value
dereference(value.ptr).a = dereference(self.c.at(index)).a
return value
I could also expose a init=True flag for each __cinit__ method, which would prevent reconstructing the object internally if the C-object exists already (when init is False). However, this could cause catastrophic issues, since it would be exposed to the Python API and would allow dereferencing a null or uninitialized pointer.
def __cinit__(MyStruct self, bint init=True):
if init:
self.ptr.reset(new mystruct)
I could also overload __init__ with the Python-exposed constructor (which would reset self.ptr), but this would have risky memory safety if __new__ was used from the Python layer.
Bottom-Line
I would love to use Cython, for compilation speed, syntactical sugar, and numerous other reasons, as opposed to the fairly clunky boost::python. I'm looking at pybind11 right now, and it may solve the compilation speed issues, but I would still prefer to use Cython.
Is there any way I can do such a simple task idiomatically in Cython? Thanks.
The way this works in Cython is by having a factory class to create Python objects out of the shared pointer. This gives you access to the underlying C/C++ structure without copying.
Example Cython code:
<..>
cdef class MyStruct:
cdef shared_ptr[mystruct] ptr
def __cinit__(self):
# Do not create new ref here, we will
# pass one in from Cython code
self.ptr = NULL
def __dealloc__(self):
# Do de-allocation here, important!
if self.ptr is not NULL:
<de-alloc>
<rest per MyStruct code above>
cdef object PyStruct(shared_ptr[mystruct] MyStruct_ptr):
"""Python object factory class taking Cpp mystruct pointer
as argument
"""
# Create new MyStruct object. This does not create
# new structure but does allocate a null pointer
cdef MyStruct _mystruct = MyStruct()
# Set pointer of cdef class to existing struct ptr
_mystruct.ptr = MyStruct_ptr
# Return the wrapped MyStruct object with MyStruct_ptr
return _mystruct
def make_structure():
"""Function to create new Cpp mystruct and return
python object representation of it
"""
cdef MyStruct mypystruct = PyStruct(new mystruct)
return mypystruct
Note the type for the argument of PyStruct is a pointer to the Cpp struct.
mypystruct then is a python object of class MyStruct, as returned by the factory class, which refers to the
Cpp mystruct without copying. mypystruct can be safely returned in def cython functions and used in python space, per make_structure code.
To return a Python object of an existing Cpp mystruct pointer just wrap it with PyStruct like
return PyStruct(my_cpp_struct_ptr)
anywhere in your Cython code.
Obviously only def functions are visible there so the Cpp function calls would need to be wrapped as well inside MyStruct if they are to be used in Python space, at least if you want the Cpp function calls inside the Cython class to let go of the GiL (probably worth doing for obvious reasons).
For a real-world example see this Cython extension code and the underlying C code bindings in Cython. Also see this code for Python function wrapping of C function calls that let go of GIL. Not Cpp but same applies.
See also official Cython documentation on when a factory class/function is needed (Note that all constructor arguments will be passed as Python objects). For built in types, Cython does this conversion for you but for custom structures or objects a factory class/function is needed.
The Cpp structure initialisation could be handled in __new__ of PyStruct if needed, per suggestion above, if you want the factory class to actually create the C++ structure for you (depends on the use case really).
The benefit of a factory class with pointer arguments is it allows you to use existing pointers of C/C++ structures and wrap them in a Python extension class, rather than always having to create new ones. It would be perfectly safe to, for example, have multiple Python objects referring to the same underlying C struct. Python's ref counting ensures they won't be de-allocated prematurely. You should still check for null when deallocating though as the shared pointer could already had been de-allocated explicitly (eg, by del).
Note that there is, however, some overhead in creating new python objects even if they do point to the same C++ structure. Not a lot, but still.
IMO this auto de-allocation and ref counting of C/C++ pointers is one of the greatest features of Python's C extension API. As all that acts on Python objects (alone), the C/C++ structures need to be wrapped in a compatible Python object class definition.
Note - My experience is mostly in C, the above may need adjusting as I'm more familiar with regular C pointers than C++'s shared pointers.

Converting C++ object to python object in cython?

I am having a hard time with my first cython program, so please don't discourage me if I have a stupid question.
This is my example:
c_foo.h:
// c_foo.h
class foo:
{
public:
....
std::unique_ptr<2DVector> get_cFoo_unique_ptr(); // Vec2D<T> is a vector of vectors of T
// std::vector<std::vector<T>>
private:
std::unique_ptr<Vec2D> cFoo_unique_ptr; // Vec2D<T> is a vector of vectors of T
}
foo.pyx:
# foo.pyx
import cython
... # other import
from cython.operator cimport unique_ptr as cpp_unique_ptr
cdef extern from c_foo.h:
cdef cppclass c_foo:
...
cpp_unique_ptr get_cFoo_unique_ptr()
cdef class py_foo:
cdef c_foo* cFoo
...
def get_Vec2D(self):
return self.cFoo.get_cFoo_unique_ptr().get()
So, what I am trying to do here is getting data from C++ unique_ptr and return them in python.
And cython complains about it.
def get_Vec2D(self):
return self.cFoo.get_cFoo_unique_ptr().get()
----------------------------------------------------------
foo.pyx:xx:xx: Cannot convert 'T *' to Python object
Another attempt is that I try other way to declare cython unique_ptr of vectors:
# foo.pyx
import cython
... # other import
from cython.memory cimport unique_ptr as cpp_unique_ptr
from cython.vector cimport vector as cpp_vector
cdef extern from c_foo.h:
cdef cppclass c_foo:
...
cpp_unique_ptr[cpp_vector[cpp_vector[T]]] get_cFoo_unique_ptr()
cdef class py_foo:
cdef c_foo* cFoo
...
def get_Vec2D(self):
return self.cFoo.get_cFoo_unique_ptr().get()
And still the same error:
Cannot convert 'vector[vector[T]] *' to Python object
So, my question is how can I access the data of C++ unique_ptr in cython?
And is there any good practice that I should know for passing data between C++ and python via cython?
Thanks
This kind of thing confused me for a long time. I have only used Cython in a C environment, not C++, so I don't know about other options that might exist in your case. But I'll explain how I do it, and hope that will be helpful.
You must create Python objects at the end of your Cython function, since those are the only valid return values. That means, among other things, that you can't return a pointer or a C array. For example, suppose I have a Cython function f that calculates a double array:
def f():
cdef double aa[1000]
cdef int i
# ....
# some calculation that populates aa
# ....
# Now I can't just return aa because aa isn't a Python object.
# I have to convert it to a Python list:
a = []
for i in range(1000):
a.append(aa[i]) # Cython knows how convert one C float to one Python float
return a

How to pass struct pointer to __cinit__ of Cython's cdef class [duplicate]

This question already has answers here:
Wrapping a pre-initialized pointer in a cython class
(3 answers)
Closed 1 year ago.
I am trying to implement cdef class with cinit which must consume cdef struct pointer.
C header
typedef struct foo *FOO;
PXD files:
extern ...:
cdef struct foo
ctypedef foo *FOO
PYX file:
cdef class ClassX(object):
def __cinit__(self, FOO arg):
...
And Cython compiler says "Cannot convert Python object argument to type 'FOO'". What is wrong and how to pass struct pointer to cinit?
Looks like I found the similar question: how to cython class function's parameter accept c++ struct. Short answer: it is not possible to pass C-struct to Python (def) function. I have to wrap a struct into Python object.
Here are some ways I managed to solve it for my case, which are very specific.
Method 1 - Convert pointer to a uint64 number (kind of dirty)
You have to make sure the reference isn't deleted before a copy, but first convert your struct pointer into an unsigned 64bit number and then cast and possibly deref back into your object. Like so
from cython.operator cimport dereference as deref
ctypedef unsigned long long uint64_t
...
cdef class SomeClass(object):
cdef FooStruct foo
def __cinit__(self, uint64_t arg):
self.foo = deref(<FooStruct*> arg) # preferably copy over keeping pointer
Then to construct it
cdef FooStruct yourfoo = ... # your struct
cdef SomeClass res
res = SomeClass(<uint64_t> &yourfoo)
It's not the most pretty solution, but it'll work for specific situations.
Method 2 - Same PYX file assignment function (preferable)
This method I found to only work with object references in the same PYX file. So if you define your cdef class with a setter function that copies the data over, which is not exposed to python, then you can update the internal struct.
cdef class SomeClass(object):
cdef FooStruct foo
def __cinit__(self):
...
cdef void _set(self, FooStruct &data):
self.foo = data # copy
Now to construct it's two lines
cdef FooStruct yourfoo = ... # your struct
cdef SomeClass res
res = SomeClass()
res._set(yourfoo)

Cython - error when trying to access contents of pointer to struct

I have a cdefed class in Cython which looks very similar to this:
cdef class AprilTagDetector:
cdef capriltag.apriltag_detector_t* _apriltag_detector
def __cinit__(self):
self._apriltag_detector = capriltag.apriltag_detector_create();
# standard null checks
# standard __dealloc__(self) here
property quad_decimate:
def __get__(self):
return self._apriltag_detector.quad_decimate
The corresponding .pxd file looks like this:
cdef extern from "apriltag.h":
# The detector itself
ctypedef struct apriltag_detector_t:
pass
# Detector constructor and destructor
apriltag_detector_t* apriltag_detector_create()
void apriltag_detector_destroy(apriltag_detector_t* td);
The problem is, when I go to compile this code, it spits out this error:
property quad_decimate:
def __get__(self):
return self._apriltag_detector.quad_decimate ^
------------------------------------------------------------
apriltags.pyx:47:14: Cannot convert 'apriltag_detector_t *' to Python object
What's going on here? I haven't been able to figure it out from the Cython docs.
I, thankfully, figured out the problem when working on this project with a friend at a hackerspace.
The problem is in the ctypedef struct apriltag_detector_t block.
When I wrote pass in the block, I thought that Cython would automatically work out the internal contents of the struct, and let me access the element(s) I needed - here, quad_decimate.
Not so.
To get Cython to understand the contents of a struct, you will have to tell it what's in the struct as so:
ctypedef struct apriltag_detector_t:
float quad_decimate

Passing a bounded method in Cython as argument

I am trying to wrap some C++ code into Cython and I came up with some trouble trying to pass a method from a class as an argument to a function.
I do not know if it makes it more clear, but class A represents a statistical model (so myAMethod uses not only the arguments passed but many instance variables) and B has different methods for minimizing the function passed.
In C++ I have something of this style:
class A
{
public:
double myAMethod(double*)
};
class B
{
public:
double myBMethod(A&, double (A::*f) (double*)
}
So what I am trying to do is to use instances of both A and B in Cython code. I had no trouble wrapping the classes, but when I try to use myBMethod, I don't know how to pass a pointer of the kind A::*myAMethod
If I do this:
myBMethod(ptrToAObj[0], &ptrToAObj.myAMethod),
then Cython compiles this code to [...] &ptrToAObj->myAMethod [...], and I get the message one would expect from g++:
"ISO C++ forbids taking the address of a bound member function to form a pointer to member function."
But if I try to point straight to the class method, and do myBMethod(ptrToAObj[0], A.myAMethod), then Cython won't compile and say that
myAMethod is not a static member from A.
And that's pretty much all I was able to advance. I could work at C++ level and avoid any of these anoyances, but if I were able to use instances of A and B in Python (via Cython) interactively, it would help me in speedig my development pace.
Any help will be really appreciated, and I apologize if this question as been already answered and/or is available in a referece - I search SO, Cython reference and Smith's "Cython" book and I did not found this theme adressed.
Thanks in advance!
I have a partial (if horrendous) solution. I'm prepared to believe there's a better way, but I don't know it.
In cpp_bit.hpp:
class A {
public:
double myAMethod(double*) { return 0.0; }
};
typedef double (A::*A_f_ptr)(double *);
class B {
public:
double myBMethod(A& a, A_f_ptr f) {
double x = 0.1;
return (a.*f)(&x);
}
};
A_f_ptr getAMethod() {
return &A::myAMethod;
}
I've given the functions very basic implementations, just so I can check for really obvious crashes. I've also created a function pointer which returns a pointer to myAMethod. You'll need to do this for every method you want to wrap.
In py_bit.pyx
# distutils: language = c++
from cython.operator import dereference
cdef extern from "cpp_bit.hpp":
cdef cppclass A:
double myAMethod(double*)
cdef cppclass A_f_ptr:
pass
cdef cppclass B:
double myBMethod(A&, A_f_ptr)
cdef A_f_ptr getAMethod()
cdef class PyA:
cdef A* thisptr
def __cinit__(self):
self.thisptr = new A()
def __dealloc__(self):
del self.thisptr
cpdef myAMethod(self,double[:] o):
return self.thisptr.myAMethod(&o[0])
cdef class PyB:
cdef B* thisptr
def __cinit__(self):
self.thisptr = new B()
def __dealloc__(self):
del self.thisptr
cpdef myBMethod(self,PyA a):
return self.thisptr.myBMethod(dereference(a.thisptr),getAMethod())
I couldn't figure out how to typedef a member function pointer in Cython, so instead I created an empty cppclass with the same name. This works because cython just seems to use it for type-checking and nothing more, and since it includes cpp_bit.hpp (where it's defined) you can use it no problem.
All I've done is left the task of getting the member function pointer to c++ (in getAMethod, which I call). I don't think it's entirely satisfactory, but it looks workable, and is only a short extra c++ function for every member function you want to access. You could play with where you put it to encapsulate it more cleanly.
An alternative, untested approach: (Edit: further thought suggests this might be very tricky! Attempt this at your own risk!)
Personally, I'd be tempted to change the c++ interface so that myBMethod is defined as
double myBMethod(std::function<double (double*)>)
(since presumably you always call it with the A instance it's passed with). Then use lambda functions in c++(11!) to wrap the A instance and function together
b.myBMethod([&](double* d){ return a.myAMethod(d) };
It may then take a bit of hugely complicated Cython wrapping that I haven't yet considered, but it should be possible to convert a simple double (double*) function pointer to the c++ function object, and so use it more directly.
It's also possible that your actual design is more complicated in ways I haven't considered, and this approach isn't flexible enough anyway.

Categories

Resources