通过Cython将Python字符串传递给C
我正在尝试写一个模块,里面有一些C语言和一些Python的部分。我使用Cython来连接这两者。
我想把我的(非常长的)字符串常量存储在Python中,因为这样写起来更简单:
const char long_string = "\npart of string\n"
"next part\n"
"last part\n";
而不是:
long_string = """
part of string
next part
last part
"""
(这些字符串比这个长得多,而且更复杂——以至于每次想编辑时,我都不想手动添加和删除"
和\n"
。实际上,它们是OpenCL内核。)
我需要能通过Cython把这些字符串转换成C字符串,根据文档,我只需要这样做:
cdef bytes py_bytes = py_string.encode()
cdef char* c_string = py_bytes
而且不需要手动管理内存,只要我保持对py_bytes
的引用,c_string
就能正常工作。
然而,我在用简单的printf测试时遇到了问题。这里是我的Cython文件:
cdef extern from "stdio.h":
printf(char* string)
def go():
py_string = """
a complicated string
with a few
newlines.
"""
cdef bytes py_bytes = py_string.encode()
cdef char* c_string = py_bytes
printf(c_string)
print "we don't get this far :("
在运行时用pyximport
编译后,终端输出如下,然后就崩溃了:
a complicated string
with a few
newlines.
Segmentation fault: 11
现在,我检查了Cython实际上在C文件中生成了什么,并在一个普通的C文件中尝试过,它没有崩溃:
#include "stdio.h"
static char __pyx_k_1[] = "\na complicated string\nwith a few\nnewlines.\n";
int main(void) {
void* output = printf(__pyx_k_1);
if (!output) {
printf("apparently, !output.");
}
}
为了明确,Cython生成的代码会捕获printf
的输出,并测试“不是那个”。变量的类型是PyObject*
。
我唯一的猜测是字符串没有正确结束,所以printf继续读取到字符串末尾之外,导致崩溃,但在我的纯C测试中并没有发生这种情况,所以我完全搞不懂了。
所以,我真正想问的是,如何从Cython真正地把C字符串传递给C代码?如果有人能指出更简单的解决我最开始提到的问题的方法,那也非常欢迎 :)
1 个回答
从 libc.stdio
导入 printf
解决了我的问题:
from libc.stdio cimport printf
def go():
py_string = """
a complicated string
with a few
newlines.
"""
cdef bytes py_bytes = py_string.encode()
cdef char* c_string = py_bytes
printf(c_string)
print "we actually got this far! :)"
错误出在 printf
的声明上。应该按照 stdio.pxd
中列出的那样,
cdef extern from *:
ctypedef char const_char "const char"
int printf(const_char *, ...)
而你的版本隐式地是 object printf(char *)
;这里的默认返回值类型是 Python 对象,而不是 C 语言中的 int
。正确的声明可以避免 Cython 尝试对 printf
的返回值进行 Py_XDECREF
操作。
(顺便说一下,在你的“普通” C 问题中,你不应该把 printf
的返回值强制转换为 void *
。)