提问人:MaxGyver 提问时间:5/2/2023 最后编辑:MaxGyver 更新时间:5/4/2023 访问量:87
如何将字符串列表传递给 CFFI 扩展?
How to pass list of strings to a CFFI extension?
问:
我想将字符串列表传递给需要 a 作为输入参数的 CFFI 扩展。char**
例:
extension.h
:
#include <stddef.h>
void sort_strings(char** arr, size_t string_count);
extension.c
:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int cmpstrp(const void *p1, const void *p2) {
return strlen(*(const char**)p1) < strlen(*(const char**)p2);
}
void sort_strings(char** arr, size_t string_count) {
qsort(arr, string_count, sizeof(char*), cmpstrp);
}
// `main` is defined only for testing if `sort_strings` works as expected
int main(void) {
char* string_list[] = {"cat", "penguin", "mouse"};
size_t string_count = sizeof(string_list)/sizeof(string_list[0]);
sort_strings(string_list, string_count);
for (int i = 0; i < string_count; i++) {
printf("%s\n", string_list[i]);
}
}
extension_build.py
:
from cffi import FFI
ffibuilder = FFI()
ffibuilder.cdef('void sort_strings(char** arr, size_t string_count);')
ffibuilder.set_source('_extension',
'#include "extension.h"',
sources=['extension.c'],
libraries=[])
if __name__ == "__main__":
ffibuilder.compile(verbose=True)
demo.py
:
from _extension.lib import sort_strings
from _extension import ffi
string_list = ["cat", "penguin", "mouse"]
bytes_list = [s.encode("latin1") for s in string_list]
cdata_list = ffi.new("char **", bytes_list)
sort_strings(cdata_list, len(string_list))
# print sorted list
如何测试:
python extension_build.py
python demo.py
我尝试将 ,并作为第一个输入参数传递给 。我收到以下错误消息:string_list
bytes_list
cdata_list
sort_strings
TypeError: initializer for ctype 'char *' must be a cdata pointer, not str
TypeError: initializer for ctype 'char *' must be a cdata pointer, not bytes
TypeError: initializer for ctype 'char *' must be a cdata pointer, not list
如何正确传递我的字符串列表(如果可能,不复制列表)?
(以防万一我的意图不明确:我不是在问如何在 Python 中对列表进行排序。
溶液:
(基于Armin Rigo的回答。
它像这样更新时起作用:demo.py
from _extension.lib import sort_strings
from _extension import ffi
string_list = ["cat", "penguin", "mouse"]
bytes_list = [ffi.new("char[]", s.encode("latin1")) for s in string_list]
pointer = ffi.new("char*[]", bytes_list)
sort_strings(pointer, len(string_list))
for s in pointer:
print(ffi.string(s).decode("latin1"))
答:
1赞
Armin Rigo
5/3/2023
#1
您需要将
cdata_list = ffi.new("char **", bytes_list)
跟
cdata_list = ffi.new("char *[]", bytes_list)
因为分配一个 single 并返回指向它的指针,而分配一个 multi 的数组。ffi.new("T*")
T
ffi.new("T[]", length_or_list)
T
编辑:
啊不,另一个问题是你必须显式分配每个,所以你需要:char *
bytes_list = [ffi.new("char[]", s.encode("latin1")) for s in string_list]
然后这可以直接作为参数传递给(或者你可以像上面一样传递,但如果你只是直接传递,CFFI 也会做同样的事情)。bytes_list
sort_string()
cdata_list
bytes_list
编辑2:
传递 or 不是 100% 等价的:如果你做了一个显式并在调用中使用它,那么你可以在调用后从中读取它,以了解 C 函数如何更改这个 C 数组(就像在你的示例中所做的那样)。如果改为传递,则 CFFI 会在调用之前将其转换为相同的 C 数组,但在调用后立即释放 C 数组。bytes_list
cdata_list
cdata_list
bytes_list
评论
0赞
MaxGyver
5/3/2023
谢谢。这将运行而不会出错。但它没有按预期工作:调用 后列表没有排序。好像收到的是列表的副本而不是指针。知道为什么吗?sort_strings
sort_strings
0赞
MaxGyver
5/3/2023
我可以验证这一点:现在我正在打印其中的列表:这里是排序的。sort_strings
0赞
MaxGyver
5/3/2023
好的,它的工作原理是这样的:创建一个指向 : 的指针。然后将此指针传递给函数:.bytes_list
pointer = ffi.new("char*[]", bytes_list)
sort_strings(pointer, len(string_list))
1赞
Armin Rigo
5/4/2023
是的,如果你传递了一个普通列表,那么它不会被调用修改(它在调用之前在内部转换为 C 数组,并且 C 数组在调用后立即释放)。但是,如果您传递一个 cdata 类型,那么它作为 C 数组将反映在 C 中所做的更改。char *[]
评论