9

I'm trying to expose c++ classes to python using cython. I wrote their definitions in *.pxd file and implemented a wrappers in *.pyx files. But I got stuck trying to pass to a function pointer to the extension type. Here is simplified example.

foo.pyx

from c_foo cimport cFoo
cdef class Foo:
    cdef cFoo* _impl

c_foo_holder.pxd

cdef extern from "FooHolder.h":
    cdef cppclass cFooHolder:
        cFooHolder(cFoo* foo)    

foo_holder.pyx

from c_foo_holder cimport cFooHolder
from c_foo cimport cFoo

cdef class FooHolder:
    cdef cFooHolder* _impl
    def __init__(self, foo):
        self._impl = new cFooHolder(<cFoo*>(foo._impl)) # error here    

But on the last line I get error "Python objects cannot be cast to pointers of primitive types". I also tried several other approaches, but nothing worked:

# error: 'Foo' is not a type identifier 
from foo import Foo
def __init__(self, Foo foo):
    self._impl = new cFooHolder(foo._impl)

# error: 'Foo' is not a type identifier 
def __init__(self, foo):
   self._impl = new cFooHolder(<Foo>(foo)._impl)

2 Answers 2

4

I found the solution. You have to tell cython that foo._impl is really cFoo* instance. That can be achieved by providing Foo definition (e.g. in foo.pxd). Afterwards you are able to cast python object to Foo and cython will know that its _impl field has type cFoo*.

foo.pxd

from c_foo cimport cFoo
cdef class Foo:
    cdef cFoo* _impl

foo.pyx

from c_foo cimport cFoo
cdef class Foo:
    # methods implementation

c_foo_holder.pxd

cdef extern from "FooHolder.h":
    cdef cppclass cFooHolder:
        cFooHolder(cFoo* foo)    

foo_holder.pyx

from c_foo_holder cimport cFooHolder
from c_foo cimport cFoo
from foo cimport Foo

cdef class FooHolder:
    cdef cFooHolder* _impl
    def __init__(self, foo):
        self._impl = new cFooHolder((<Foo?>foo)._impl)  
Sign up to request clarification or add additional context in comments.

Comments

0

__init__ constructor can accept only pure python objects, there is also __cinit__ but it can accept only C primitive types.

When you pass Foo to __init__ it cannot see the _impl member, only members of python types can be seen.

The solution would be to pass cFoo* explicitly to an Init() method which emulates a constructor:

cdef class FooHolder:
    cdef cFooHolder* _impl
    cdef void Init(self, cFoo* foo_impl):
        self._impl = new cFooHolder(foo_impl)

Call it this way:

fooHolder = new FooHolder()
fooHolder.Init(foo._impl)

4 Comments

Seems, that I can't do that either while foo is a python object. Am I missing something? I get 'cannot convert Python object argument to type 'cFoo *' if I define function with 'def', and I can't call it from python if I define it with 'cdef'.
@DikobrAz Hmm seems that you can't call cdef methods outside of a class, try setting class members through a global function instead of an Init() method, see this example: stackoverflow.com/a/12205374/623622
No luck. To my knowledge, I can't call cdef members from python at all, they are accessible only inside cython module. And I can't (don't know how) convert python object to cython pointer, so that is actually the problem.
@DikobrAz You can't call cdef functions from python.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.