在C ++中嵌入Cython类方法

时间:2022-10-11 20:27:02

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:

我试图在C ++类中嵌入一个Cython类。考虑到项目的约束,不可能为此C ++类创建Cython包装器。由于Cython类中的方法数量和Cython类的长继承性,完全从类中删除方法并不是一个有吸引力的解决方案。我有必要创建一个Cython类实例并从C ++调用它的方法。但是,我似乎无法让它不是段错误。以下是问题的示例:

<<< File: fooClass.pyx >>>

<< <文件: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 >>>

<< <文件: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 >>>

<< <文件: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!

我用python setup.py build_ext构建--inplace并用g ++编译。通过测试,我知道Py_Initialize()和import_fooClass正在成功。而且我知道我在foobar()中打印了foo.a和foo.b的值,但是一旦我使用foobar()中的Foo对象进行调用,程序会出现段错误。即使在foobar()内调用foo .__ dict__或foo.callable()也会导致段错误。更改public或api关键字已生效,__init__和__cinit__之间也没有切换。如果有人知道如何解决这个问题,我会非常感激。我怀疑它与指针或滥用Python C API有关。非常感谢!

2 个解决方案

#1


1  

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:

我设法解决了这个问题。基于David W所说的内容(感谢David!),我创建了另一个cdef api类来充当构造函数的包装器,cdef api Foo buildFoo(double a,double b):这会返回一个Foo *指针,这是必需的通过.pyx文件中的foobar(Foo foo,double d)。生成的文件如下所示:

<<< File: fooClass.pyx >>>

<< <文件: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 >>>

<< <文件: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.

使用相同的setup.py脚本。

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.

运行此结果将-0.262375打印到stdout,这是正确的结果。我希望在我的代码中使用更复杂的这个想法来替换对boost :: python的一些调用,以提高性能。

#2


1  

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.

在更复杂的设置中使用前面提到的技术并反复获取段错误后,这是我使用的替代方法。关键区别在于pyx文件中的api和public关键字以及如何在foo.cpp中包含和使用该类。

<<< File: fooClass.pyx >>>

<< <文件: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 >>>

<< <文件: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();
}

#1


1  

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:

我设法解决了这个问题。基于David W所说的内容(感谢David!),我创建了另一个cdef api类来充当构造函数的包装器,cdef api Foo buildFoo(double a,double b):这会返回一个Foo *指针,这是必需的通过.pyx文件中的foobar(Foo foo,double d)。生成的文件如下所示:

<<< File: fooClass.pyx >>>

<< <文件: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 >>>

<< <文件: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.

使用相同的setup.py脚本。

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.

运行此结果将-0.262375打印到stdout,这是正确的结果。我希望在我的代码中使用更复杂的这个想法来替换对boost :: python的一些调用,以提高性能。

#2


1  

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.

在更复杂的设置中使用前面提到的技术并反复获取段错误后,这是我使用的替代方法。关键区别在于pyx文件中的api和public关键字以及如何在foo.cpp中包含和使用该类。

<<< File: fooClass.pyx >>>

<< <文件: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 >>>

<< <文件: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();
}