2

I'm trying to run a cython example which is a bit more complex than the one which can be found in many tutorials (e.g. this guide ).

Here are a minimal example (please don't mind it doesn't have much functionality) and steps to reproduce my problem:

There are c++-classesRectangle and Group2 (I put here everything into the .h file to make it shorter):

// Rectangle.h
namespace shapes { 
    class Rectangle {
        public:
            Rectangle() {}
    };

    class Group2 {
    public:
        Group2(Rectangle rect0, Rectangle rect1) {}
    };
}

Then I create a grp2.pyx file (in the same folder as the above header), with wrappers for Rectangle and Group2:

# RECTANGLE
cdef extern from "Rectangle.h" namespace "shapes":
    cdef cppclass Rectangle:
        Rectangle() except +

cdef class PyRectangle:
    cdef Rectangle c_rect
    def __cinit__(self):
        self.c_rect = Rectangle()

# GROUP2
cdef extern from "Rectangle.h" namespace "shapes":
    cdef cppclass Group2:
        Group2(Rectangle rect0, Rectangle rect1) except +    

cdef class PyGroup2:
    cdef Group2 c_group2
    def __cinit__(self, Rectangle rect0, Rectangle rect1):
        self.c_group2 = Group2(rect0, rect1)

The extension is built via a setup.py file that I call from command line (python setup.py build_ext -i):

from distutils.core import setup, Extension
from Cython.Build import cythonize

setup(ext_modules = cythonize(Extension(
           name="grp2",                                # the extension name
           sources=["grp2.pyx"], # the Cython source 
           language="c++",                        # generate and compile C++ code
      )))

At this point I have the error in the _cinint_ of PyGroup2:

Cannot convert Python object argument to type 'Rectangle'

I suppose there is some mistake in my pyx file, but I cannot tell what.

0

1 Answer 1

4

You should use PyRectangle in the signatures of def-functions and PyRectangle.c_rect when passing rectangles to C++-functions.

That means your code should be:

cdef class PyGroup2:
    ...
    def __cinit__(self, PyRectangle rect0, PyRectangle rect1):
        self.c_group2 = Group2(rect0.c_rect, rect1.c_rect)

Read on for a more detailed explanation why.


All arguments passed to def-functions are Python-objects (i.e. of type object in Cython-parlance), after all those functions will be called from pure Python, which only knows Python-objects.

However, you can add some syntactic sugar and use "late-binding" in the signature of a def-function, for example, instead of

def do_something(n):
  ...

use

def do_something(int n):
  ...

Under the hood, Cython will transform this code to something like:

def do_something(n_):
   cdef int n = n_ # conversion to C-int
   ...

This automatic conversion is possible for builtin-types like int or double, because there is functionality in Python-C-API for these conversions (i.e. PyLong_AsLong, PyFloat_AsDouble). Cython also handles the error checking, so you should not undertake these conversion manually.

However, for user-defined types/classes like your Rectangle-class such automatic conversion is not possible - Cython can only automatically convert to cdef-classes/extensions, i.e. PyRectangle, thus PyRectangle should be used in the signature:

cdef class PyGroup2:
    ...
    def __cinit__(self, PyRectangle rect0, PyRectangle rect1):
        ...

After Cython took care of conversion from object to PyRectangle, the last step from PyRectangle to Rectangle must be taken manually by utilizing the c_rect - pointer:

...
def __cinit__(self, PyRectangle rect0, PyRectangle rect1):
    self.c_group2 = Group2(rect0.c_rect, rect1.c_rect)

The rules are similar for cpdef-function, because they can be called from pure Python. The "early binding" works only for types which Cython can automatically coverted from/to Python objects.

Unsurprisingly, the only function which can habe C++-classes in their signatures are the cdef-functions.

Sign up to request clarification or add additional context in comments.

1 Comment

Very clear answer. This crucial point was not so easy to understand from the other sources I looked. Thank you

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.