在Cython中将C结构封装/转换为Python类

5 投票
1 回答
2501 浏览
提问于 2025-04-18 05:51

我刚开始学习Cython,想把一些C库中的结构体封装成Python的方法和类。不过我不太明白的是,如何将(已经初始化的)C结构体转换成对应的Python类。这里我缺少了什么呢:

这是C头文件中的一段代码:

struct test_struct {
    int _something;
    complex_struct* _another;
};
typedef struct test_struct test;

test *test_new(void);
int some_method(test **n, int irrelevant);

这是我.pxd文件中的对应代码:

cdef struct test_struct:
    pass
ctypedef test_struct test

test* test_new()
int some_method(test **n, int irrelevant)

我的.pyx文件:

def do_something(int irrelevant):
    cdef test* t = test_new()
    ret = some_method(&t, irrelevant)

    # Here comes the problem...
    return <Test?>t

cdef class Test:  
    cdef test* _t

    # cinit here and some methods here. No members except _t

在返回语句之前的部分都运行得很好。我在ret中得到了正确的值等等。但是返回语句中的转换似乎不正确,或者缺少了更多的信息。当我执行t = do_something(42)时,Python就崩溃了。

崩溃的错误信息一点帮助都没有:

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7a9e74b in internal_print () from /usr/lib/libpython2.7.so.1.0
(gdb) bt
#0  0x00007ffff7a9e74b in internal_print () from /usr/lib/libpython2.7.so.1.0
#1  0x00007ffff7a81adb in PyFile_WriteObject () from /usr/lib/libpython2.7.so.1.0
#2  0x00007ffff7b19acb in sys_displayhook () from /usr/lib/libpython2.7.so.1.0
#3  0x00007ffff7a64c23 in PyObject_Call () from /usr/lib/libpython2.7.so.1.0
#4  0x00007ffff7af2ff7 in PyEval_CallObjectWithKeywords () from /usr/lib/libpython2.7.so.1.0
#5  0x00007ffff7af6f3b in PyEval_EvalFrameEx () from /usr/lib/libpython2.7.so.1.0
#6  0x00007ffff7af91b0 in PyEval_EvalCodeEx () from /usr/lib/libpython2.7.so.1.0
#7  0x00007ffff7af92b2 in PyEval_EvalCode () from /usr/lib/libpython2.7.so.1.0
#8  0x00007ffff7b11f9f in run_mod () from /usr/lib/libpython2.7.so.1.0
#9  0x00007ffff7b13ec0 in PyRun_InteractiveOneFlags () from /usr/lib/libpython2.7.so.1.0
#10 0x00007ffff7b140ae in PyRun_InteractiveLoopFlags () from /usr/lib/libpython2.7.so.1.0
#11 0x00007ffff7b1470e in PyRun_AnyFileExFlags () from /usr/lib/libpython2.7.so.1.0
#12 0x00007ffff7b24bcf in Py_Main () from /usr/lib/libpython2.7.so.1.0
#13 0x00007ffff746f000 in __libc_start_main () from /usr/lib/libc.so.6
#14 0x000000000040073e in _start ()

正如你所看到的,do_something方法应该返回一个类型为Test的Python对象。我需要添加什么才能让转换成功呢?我需要手动将结构体映射到Python类吗?有没有什么Cython的技巧可以使用?

1 个回答

5

你需要把 Test 变成一个真正的 C 类型的包装器。不过,你不能直接把 C 的参数传给 Python 的函数(比如构造函数)。所以你还需要一个工厂函数。下面是一个例子:

cdef class Test:
    cdef test* _t

    def __cinit__(self):
        self._t = NULL

    def _setup(self, test* t):
        self._t = t
        return self

    property something:
        def __get__(self):
            return self._t._something
        def __set__(self, int val):
            self._t._something = val

cdef Test_create(test* t):
    return Test()._setup(t)

然后在 do_something() 里:

return Test_create(t)

撰写回答