cdef extern from "Foo.h":
cdef cppclass Bar:
pass
cdef class PyClass:
cdef Bar *bar
def __cinit__(self, Bar *b)
bar = b
This will always give me something like:
Cannot convert Python object argument to type 'Bar *'
Is there a way to accomplish this, or do I need to extract everything from a Bar object, create a Python equivalent, pass it in, then reconstruct it in PyClass?
I came across this problem trying to wrap C code with structs as python classes. The issue seems to be that "special" function including __init__ and __cinit__ must be declared as def rather than cdef. This means that they can be called from normal python, so the type parameters are effectively ignored and everything is treated as object.
In J.F. Sebastian's answer the fix is not the wrapping - a double is a basic numeric type and so there is a default conversion between the C/C++ type and the Python object. Czarek's answer is basically correct - you need to use a fake constructor idiom, using a global function. It is not possible to use a #staticmethod decorator as they cannot be applied to cdef functions. The answer looks simpler on the original example provided.
cdef extern from "Foo.h":
cdef cppclass Bar:
pass
cdef class PyClass:
cdef Bar *bar
cdef PyClass_Init(Bar *b):
result = PyClass()
result.bar = b
return result
As of Cython 0.21 it has been possible to declare cdef methods with the #staticmethod decorator. This allows static creator methods that take non-Python arguments:
cdef extern from "Foo.h":
cdef cppclass Bar:
pass
cdef class PyClass:
cdef Bar *bar
#staticmethod
cdef create(Bar *bar):
cdef PyClass pc = PyClass()
pc.bar = bar
return pc
For each cdef class create a global cdef function that acts as a constructor, CefResponse is a C++ object, PyResponse a python equivalent of a c++ object:
cdef object CreatePyResponse(CefRefPtr[CefResponse] cefResponse):
pyResponse = PyResponse()
pyResponse.cefResponse = cefResponse
return pyResponse
cdef class PyResponse:
cdef CefRefPtr[CefResponse] cefResponse
def GetStatus(self):
return (<CefResponse*>(self.cefResponse.get())).GetStatus()
So instead of resp = PyResponse(cppObject) call resp = CreatePyResponse(cppObject).
Example taken from CEF Python:
https://code.google.com/p/cefpython/source/browse/cefpython/response.pyx?r=0250b65e046a
Python class accepts Python arguments. To pass a C++ argument you need to wrap it:
# distutils: language = c++
cdef extern from "Foo.h" namespace "baz":
cdef cppclass Bar:
Bar(double d)
double get()
cdef class PyBar: # wrap Bar class
cdef Bar *thisptr
def __cinit__(self, double d):
self.thisptr = new Bar(d)
def __dealloc__(self):
del self.thisptr
property d:
def __get__(self):
return self.thisptr.get()
PyBar instances can be used as any other Python objects both from Cython and pure Python:
class PyClass:
def __init__(self, PyBar bar):
self.bar = bar
print(PyClass(PyBar(1)).bar.d)
Related
I have a function with void * parameter and I want to store it in the dict.
What I do is:
%%cython
cdef void foo(void * bar):
pass
cdef dict foobar = {'foo': foo}
But this code raises an Error: Cannot convert 'void (void *)' to Python object Any ways to overcome this problem?
The easiest solution is to create a cdef class that can wrap this function. Since the cdef class is a Python object it can be stored in a dictionary like any other Python object.
ctypedef void (*void_func_ptr)(void*)
cdef class VoidFuncWrapper:
cdef void_func_ptr func
def __cinit__(self):
self.func = NULL
#staticmethod
cdef VoidFuncWrapper make_from_ptr(void_func_ptr f):
cdef VoidFuncWrapper out = VoidFuncWrapper()
out.func = f
return out
Then you can simply do:
cdef dict foobar = {'foo': VoidFuncWrapper.make_from_ptr(foo)}
Your problem here is that void* has no defined converter from a Python object, so a Python wrapper function (that accepts Python objects, converts them, and passes them to the underlying C function) can't be defined, either explicitly with cpdef or implicitly by putting the cdef function in a Python object (the dict in this case).
You could make this work by defining the argument to be something Cython knows how to convert to, e.g.:
cpdef void foo(const char * bar):
pass
cdef dict foobar = {'foo': foo}
Try it online!
But that may not work for your scenario if the function needs to accept an arbitrary pointer. If that's the case, you may want to switch to using C++ containers that can hold your function pointer type directly:
from libcpp.unordered_map cimport unordered_map
from libcpp.string cimport string
cdef void foo(void *bar):
pass
ctypedef void (*f_type)(void *)
cdef unordered_map[string, f_type] foobar
foobar['foo'.encode('ascii')] = foo
I have the following code in my Cython wrapper to a C++ code:
# distutils: language = c++
# distutils: sources = symbolic.cpp
from libcpp.vector cimport vector
from libcpp.pair cimport pair
from libcpp.string cimport string
from libcpp cimport bool
cdef extern from "symbolic.h" namespace "metadiff::symbolic":
cdef cppclass SymbolicMonomial:
vector[pair[int, int]] powers
long long coefficient;
SymbolicMonomial()
SymbolicMonomial(long)
SymbolicMonomial(const SymbolicMonomial&)
bool is_constant()
long long int eval(vector[int]&)
long long int eval()
string to_string()
string to_string_with_star() const
cdef SymbolicMonomial mul_mm"operator*"(const SymbolicMonomial&, const SymbolicMonomial&)
# SymbolicMonomial operator*(long long, const SymbolicMonomial&)
# SymbolicMonomial operator*(const SymbolicMonomial&, long long)
cdef class SymMonomial:
cdef SymbolicMonomial* thisptr # hold a C++ instance which we're wrapping
def __cinit__(self):
self.thisptr = new SymbolicMonomial()
def __cinit__(self, int value):
self.thisptr = new SymbolicMonomial(value)
def __dealloc__(self):
del self.thisptr
def is_constant(self):
return self.thisptr.is_constant()
def eval(self):
return self.thisptr.eval()
def __str__(self):
return self.to_string_with_star()
def to_string(self):
return self.thisptr.to_string().decode('UTF-8')
def to_string_with_star(self):
return self.thisptr.to_string_with_star().decode('UTF-8')
def __mul__(self, other):
return mul_mm(self.thisptr, other)
def variable(variable_id):
monomial = SymMonomial()
monomial.thisptr.powers.push_back((variable_id, 1))
return monomial
However, I never figured it out how to call the mul_mm method correctly. It keeps saying Cannot convert 'SymbolicMonomial' to Python object or vice versa. The thing is I need to be able to multiply two SymMonomials in this way. However for some reason I can not get the hang of it of how to do it properly. Any advices?
You have a number of issues:
You can't return C++ objects directly to Python - you need to return your wrapper type (assign to thisptr of the wrapper)
You can't guarantee either self or other is of the correct type at the point the function is called (see the note in http://docs.cython.org/src/userguide/special_methods.html#arithmetic-methods about how the methods can be called with the operands in either order). To use the C/C++ members of a Cython class you need to ensure Cython knows that object is indeed of that class. I recommend using the <Classname?> style cast (note the question mark) which throws an exception if it doesn't match.
You need to get thisptr from other too, rather than just passing the Python wrapper class to your C++ function.
The following should work.
def __mul__(self,other):
cdef SymMonomial tmp = SymMonomial()
cdef SymMonomial self2, other2
try:
self2 = <SymMonomial?>self
other2 = <SymMonomial?>other
except TypeError:
return NotImplemented # this is what Python expects for operators
# that don't know what to do
tmp.thisptr[0] = mul_mm(self2.thisptr[0],other2.thisptr[0])
return tmp
I am trying to write a Cython wrapper around a C library. I am very new to Cython, so my apologies in advance if the problem is obvious.
In a file wrapper.pxd, I define a struct (reduced example):
cdef extern from "thiscouldbeyourlibrary.h":
cdef struct foo:
double **output
I then have a class:
cdef class Bar:
cdef wrapper.foo __stuff
cdef do_something(self):
self.__stuff.output = NULL
This fails:
Cannot convert 'void *' to Python object.
Apparently, Cython cannot be sure that self.__stuff.output is always a pointer. But I declared its type and the class is a "cdef" class, so I don't really understand why.
The problem is the incompatibility between NULL and double **. You could assign it to a char, int or void *, for example, doing:
wrapper.pyd:
cdef extern from "thiscouldbeyourlibrary.h":
cdef struct foo:
char a
int b
void *c
double **output
thiscouldbeyourlibrary.h:
struct foo
{
char a;
int b;
void *c;
double **output;
};
main.pyx:
cimport wrapper
cdef class Bar:
cdef wrapper.foo __stuff
def __init__(self):
self.__stuff.a = <char>NULL
self.__stuff.b = <int>NULL
self.__stuff.c = NULL
def main():
bar = Bar()
print bar.__stuff.a
print bar.__stuff.b
If you had allocated the memory for output before, you could have done it:
self.__stuff.output[0] = NULL
without allocating it will crash...
The situation is as follows: I want to wrap the method Unit.getDistance
//Unit.h
class Unit{
....
int getDistance(PositionOrUnit target) const;
};
//PositionUnit.h
class PositionOrUnit{
PositionOrUnit(Unit unit = nullptr);
PositionOrUnit(Position pos);
};
The library uses converting constructors to allow Unit or Position to be automatically constructed into a PositionOrUnit object by the compiler, so that it is possible to pass a Unit or Position object directly into this method.
#cUnit.pxd
ctypedef UnitInterface *Unit
cdef cppclass UnitInterface:
int getDistance(PositionOrUnit target) const
#cPositionUnit.pxd
cdef cppclass PositionOrUnit:
PositionOrUnit() #fake for nullptr unit
PositionOrUnit(Unit unit)
PositionOrUnit(Position pos)
Now I don't know how to make a converting constructor in Python, so I use subclass polymorphism to get this to work. I declare a base class PositionUnitConverter and have Unit and Position subclass from it.
#PositionUnit.pyx
cdef class PositionUnitConverter:
cdef cPositionUnit.PositionOrUnit getPositionOrUnit(self):
return cPositionUnit.PositionOrUnit()
cdef class PositionOrUnit(PositionUnitConverter):
cdef cPositionUnit.PositionOrUnit thisobj
cdef cPositionUnit.PositionOrUnit getPositionOrUnit(self):
return self.thisobj
#Unit.pyx
cdef class Unit(PositionUnitConverter):
cdef cUnit.Unit thisptr
cdef cPositionUnit.PositionOrUnit getPositionOrUnit(self):
return cPositionUnit.PositionOrUnit(self.thisptr)
def getDistance(self, PositionUnitConverter target):
return self.thisptr.getDistance(target.getPositionOrUnit())
The end result is that in my Python code I can call Unit.getDistance with either type of object still.
# this works
unit.getDistance(unit2)
But now I get several of these warnings:
CyBW\CyBW.cpp(15698) : warning C4190: 'abstract declarator' has C-linkage specified, but
returns UDT 'BWAPI::PositionOrUnit' which is incompatible with C
../include/BWAPI/PositionUnit.h(13) : see declaration of 'BWAPI::PositionOrUnit'
The .cpp code generated at that line is: (split to be easier to read)
__pyx_vtable_4CyBW_5BWAPI_PositionUnitConverter.getPositionOrUnit = (BWAPI::PositionOrUnit
(*)(struct __pyx_obj_4CyBW_5BWAPI_PositionUnitConverter *))
__pyx_f_4CyBW_5BWAPI_21PositionUnitConverter_getPositionOrUnit;
My Questions are:
Am I doing something wrong to get this warning
How can I avoid this warning?
or should I ignore this warning, and how?
If there is anything else I can provide to help, please comment.
This question already has answers here:
Wrapping a pre-initialized pointer in a cython class
(3 answers)
Closed 1 year ago.
I'm trying to wrap two C++ classes: Cluster and ClusterTree. ClusterTree has a method get_current_cluster() that instantiates a Cluster object, and returns a reference to it. ClusterTree owns the Cluster object, and manages its creation and deletion in C++.
I've wrapped Cluster with cython, resulting in PyCluster.
PyCluster should have two ways of creation:
1) By passing in two arrays, which then implies that Python should then automatically handle deletion (via __dealloc__)
2) By directly passing in a raw C++ pointer (created by ClusterTree's get_current_cluster()). In this case, ClusterTree then assumes responsibility of deleting the underlying pointer.
from libcpp cimport bool
from libcpp.vector cimport vector
cdef extern from "../include/Cluster.h" namespace "Terran":
cdef cppclass Cluster:
Cluster(vector[vector[double]],vector[int]) except +
cdef class PyCluster:
cdef Cluster* __thisptr
__autoDelete = True
def __cinit__(self, vector[vector[double]] data, vector[int] period):
self.__thisptr = new Cluster(data, period)
#classmethod
def __constructFromRawPointer(self, raw_ptr):
self.__thisptr = raw_ptr
self.__autoDelete = False
def __dealloc__(self):
if self.__autoDelete:
del self.__thisptr
cdef extern from "../include/ClusterTree.h" namespace "Terran":
cdef cppclass ClusterTree:
ClusterTree(vector[vector[double]],vector[int]) except +
Cluster& getCurrentCluster()
cdef class PyClusterTree:
cdef ClusterTree *__thisptr
def __cinit__(self, vector[vector[double]] data, vector[int] period):
self.__thisptr = new ClusterTree(data,period)
def __dealloc__(self):
del self.__thisptr
def get_current_cluster(self):
cdef Cluster* ptr = &(self.__thisptr.getCurrentCluster())
return PyCluster.__constructFromRawPointer(ptr)
This results in:
Error compiling Cython file:
------------------------------------------------------------
...
def get_current_cluster(self):
cdef Cluster* ptr = &(self.__thisptr.getCurrentCluster())
return PyCluster.__constructFromRawPointer(ptr)
^
------------------------------------------------------------
terran.pyx:111:54: Cannot convert 'Cluster *' to Python object
Note I cannot cdef __init__ or #classmethods.
Pointers can only be passed to cdef'd functions as arguments, and cinit has to be def'd. But providing a classmethod is almost the way to go!
cdef Cluster* __thisptr
cdef bool __wrapped ## defaults to False
#staticmethod
cdef PyCluster wrap(Cluster* ptr):
cdef PyCluster pc = PyCluster([], []) ## Initialize as cheaply as possible
del pc.__thisptr ## delete the old pointer to avoid memory leaks!
pc.__thisptr = ptr
pc.__wrapped = True
return pc
I know this is an old question, but after my own recent struggles with Cython I thought I'd post an answer for the sake of posterity.
It seems to me you could use a copy constructor to create a new PyCluster object from an existing Cluster object.
Define the copy constructor in your C code, then call the copy constructor in the Python class definition (in this case, when a pointer is passed) using new. This will work, although it may not be the best or most performant solution.