3
votes

I am trying to embed a Cython class in a C++ class. Creating a Cython wrapper for this C++ class is not possible given the constraints of the project. And due to the number of methods in the Cython classes and long inheritances of the Cython classes, removing the method from a class entirely is not an attractive solution. It is necessary for me to create a Cython class instance and call its method from C++. However, I can't seem to make it not segfault. Here is an example of the problem:

<<< File: fooClass.pyx >>>

from math import sin
cdef public class Foo[object Foo, type fooType]:
    cdef double a,b  
    def __cinit__(self, double a, double b):
        self.a = a
        self.b = b  
    cdef double bar(self, double c):
        return sin(self.a*c)  
cdef api double foobar(Foo foo, double d):
    return foo.bar(d)

<<< File: Foo.cpp >>>

#include "fooClass_api.h"
#include <iostream>

int main(){
    Py_Initialize();
    import_fooClass();
    Foo foo;
    foo.a = 1.0;
    foo.b = 10.0;
    std::cout << foobar(&foo,5.0) << "\n";
    Py_Finalize();
}

<<< File: setup.py >>>

from distutils.core import setup
from Cython.Build import cythonize  
setup ( ext_modules = cythonize ("cyClass.pyx"))

I build with python setup.py build_ext --inplace and compile with g++. Through testing I know that Py_Initialize() and import_fooClass is succeeding. And I know that I print the values of foo.a and foo.b inside of foobar(), but as soon as I make a call using the Foo object inside foobar(), the program segfaults. Even a call to foo.__dict__ or foo.callable() inside foobar() causes it to segfault. Changing the public or api keywords have had effect, nor has switching between __init__ and __cinit__. If anyone knows how to fix this, I'd be very appreciative. I suspect it has something to do with pointers or misusing the Python C API. Thanks so much!

2
In part it's because you aren't initialising Foo - it has a pointer called <something>vtab<something> which is never set, for example (see "FooClass.h"). I think you need a capi function that returns a Foo. However that still doesn't get rid of the segmentation fault for me, and I can't immediately see what's needed to fix it... - DavidW

2 Answers

1
votes

I managed to fix the problem. Based on what David W said (thanks David!), I created another cdef api class to act as a wrapper for the constructor, cdef api Foo buildFoo (double a, double b): This returns a Foo* pointer, which what is required by foobar(Foo foo, double d) in the .pyx file. The resulting files look like this:

<<< File: fooClass.pyx >>>

from math import sin

cdef public class Foo[object Foo, type fooType]:
    cdef double a,b  

    def __cinit__(self, double a, double b):
        self.a = a
        self.b = b  

    cdef double bar(self, double c):
        return sin(self.a*c)  

cdef api Foo buildFoo(double a, double b):
    return Foo(a,b)

cdef api double foobar(Foo foo, double d):
    return foo.bar(d)

<<< File: Foo.cpp >>>

#include "fooClass_api.h"
#include <iostream>

int main(){
    Py_Initialize();
    import_fooClass();
    Foo *foo = buildFoo(10.0,5.0);
    std::cout << foobar(foo,5.0) << "\n";
    Py_Finalize();
}

Using the same setup.py script.

Running this results in -0.262375 printed to stdout, which is the correct result. I hope to use a more complicated version of this idea to replace some calls to boost::python throughout my code to improve performance.

1
votes

After using the previously-mentioned technique in a more complicated setting and repeatedly getting segfaults, here is the alternate method I used. The key difference is in the api and public keywords in the pyx file and how the class is included and used in foo.cpp.

<<< File: fooClass.pyx >>>

from math import sin

cdef public class Foo[object Foo, type fooType]:
    cdef double a,b  
    def __cinit__(self, double a, double b):
        self.a = a
        self.b = b  
    cdef double bar(self, double c):
        return sin(self.a*c)  

cdef public Foo buildFoo(double a, double b):
    return Foo(a,b)

cdef public double foobar(Foo foo, double d):
    return foo.bar(d)

<<< File: Foo.cpp >>>

#include <Python.h>
#include "fooClass.h"
#include <iostream>

int main(){
    Py_Initialize();
    initfooClass();
    Foo *foo = buildFoo(10.0,5.0);
    std::cout << foobar(foo,5.0) << std::endl;
    Py_Finalize();
}