将Cython类方法嵌入C++中

3
我正在尝试将一个Cython类嵌入到一个C++类中。由于项目的限制,无法为该C++类创建一个Cython包装器。另外,由于Cython类的方法数量和长时间继承,从一个类中完全删除该方法并不是一个有吸引力的解决方案。我需要创建一个Cython类实例并从C++中调用其方法,但似乎无法避免segfault错误。以下是该问题的示例代码:

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

我使用 python setup.py build_ext --inplace 进行构建,并使用 g++ 进行编译。测试后发现,Py_Initialize()import_fooClass 调用成功。我知道在 foobar() 中可以打印出 foo.afoo.b 的值,但是一旦我在 foobar() 中使用 Foo 对象进行调用,程序就会崩溃。即使在 foobar() 中调用 foo.__dict__ 或者 foo.callable() 也会导致崩溃。更改关键字 public 或者 api 都没有效果,而且在 __init____cinit__ 之间切换也没有用。如果有人知道如何解决这个问题,我将非常感激。我怀疑这与指针或误用 Python C API 有关。非常感谢!

部分原因是因为您没有初始化“Foo” - 它有一个指针叫做<something>vtab<something>,它从未被设置,例如(请参见“FooClass.h”)。我认为您需要一个返回Foo的capi函数。然而,对于我来说仍然无法消除分段错误,并且我不能立即看出需要修复什么... - DavidW
2个回答

1
我成功解决了这个问题。根据David W的建议(感谢David!),我创建了另一个cdef api类作为构造函数的包装器,cdef api Foo buildFoo (double a, double b): 这将返回一个Foo*指针,这是.pyx文件中foobar(Foo foo, double d)所需的内容。生成的文件如下所示:

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

使用相同的setup.py脚本。

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


这差不多是我尝试过但未能使其工作的内容!很高兴你能解决它... - DavidW

1
在更复杂的环境中使用之前提到的技术时,我反复遇到段错误,这里是我使用的替代方法。关键区别在于 pyx 文件中的 apipublic 关键字以及如何在 foo.cpp 中包含和使用类。

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

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

似乎这是用Python 2完成的:initfooClass是PyInit_fooClass。无论如何它都会崩溃。 - jokoon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接