如何使用cffi嵌入在C语言中返回字符串的Python函数? [英] How can I embed a Python function that returns a string in C using cffi?
问题描述
我正在尝试使用PyPy和cffi将Python函数嵌入C中.我正在遵循PyPy文档中的本指南.
I'm trying to embed a Python function in C using PyPy and cffi. I'm following this guide from the PyPy documentation.
问题是,我发现的所有示例都在ints上运行,并且我的函数接收一个字符串并返回一个字符串.我似乎无法弄清楚如何将此函数嵌入C中,因为C似乎并没有真正的字符串,而是使用char数组.
The problem is, all the examples I've found operate on ints, and my function takes a string and returns a string. I can't seem to figure out how to embed this function in C, as C doesn't seem to really have strings, rather making do with arrays of chars.
这是我尝试过的:
# interface.py
import cffi
ffi = cffi.FFI()
ffi.cdef('''
struct API {
char (*generate_cool_page)(char url[]);
};
''')
...
@ffi.callback("char[] (char[])")
def generate_cool_page(url):
# do some processing with BS4
return str(soup)
def fill_api(ptr):
global api
api = ffi.cast("struct API*", ptr)
api.generate_cool_page = generate_cool_page
-
// c_tests.c
#include "PyPy.h"
#include <stdio.h>
#include <stdlib.h>
struct API {
char (*generate_cool_page)(char url[]);
};
struct API api; /* global var */
int initialize_api(void)
{
static char source[] =
"import sys; sys.path.insert(0, '.'); "
"import interface; interface.fill_api(c_argument)";
int res;
rpython_startup_code();
res = pypy_setup_home(NULL, 1);
if (res) {
fprintf(stderr, "Error setting pypy home!\n");
return -1;
}
res = pypy_execute_source_ptr(source, &api);
if (res) {
fprintf(stderr, "Error calling pypy_execute_source_ptr!\n");
return -1;
}
return 0;
}
int main(void)
{
if (initialize_api() < 0)
return 1;
printf(api.generate_cool_page("https://example.com"));
return 0;
}
当我运行gcc -I/opt/pypy3/include -Wno-write-strings c_tests.c -L/opt/pypy3/bin -lpypy3-c -g -o c_tests
然后运行./c_tests
时,出现此错误:
When I run gcc -I/opt/pypy3/include -Wno-write-strings c_tests.c -L/opt/pypy3/bin -lpypy3-c -g -o c_tests
and then run ./c_tests
, I get this error:
debug: OperationError:
debug: operror-type: CDefError
debug: operror-value: cannot render the type <char()(char *)>: it is a function type, not a pointer-to-function type
Error calling pypy_execute_source_ptr!
我对C没有太多的经验,我觉得我在错误地表示字符串参数/返回值.如何正确执行此操作?
I don't have a ton of experience with C and I feel like I'm misrepresenting the string argument/return value. How do I do this properly?
感谢您的帮助!
推荐答案
请注意,您不应使用pypy弃用的界面进行嵌入;相反,请参见 http://cffi.readthedocs.io/en/latest/embedding.html .
Note that you should not be using pypy's deprecated interface to embedding; instead, see http://cffi.readthedocs.io/en/latest/embedding.html.
C语言没有字符串",而只有字符数组.在C语言中,通常要编写一个要返回字符串"的函数
不同的是:它接受指向预先存在的缓冲区(类型为char[]
)的指针作为第一个参数,而接受该缓冲区的长度作为第二个参数;并在调用时填充缓冲区.这可能很杂乱,因为理想情况下,您需要在调用方中处理缓冲区太小的情况,例如分配更大的数组,然后再次调用该函数.
The C language doesn't have "strings", but only arrays of chars. In C, a function that wants to return a "string" is usually written
differently: it accepts as first argument a pointer to a pre-existing buffer (of type char[]
), and as a second argument the length of that buffer; and when called, it fills the buffer. This can be messy because you ideally need to handle buffer-too-small situations in the caller, e.g. allocate a bigger array and call the function again.
或者,某些函数放弃并返回新的malloc()
-ed char *
.然后,呼叫者必须记住要free()
它,否则会发生泄漏.在这种情况下,我建议使用这种方法,因为在调用之前猜测字符串的最大长度可能很困难.
Alternatively, some functions give up and return a freshly malloc()
-ed char *
. Then the caller must remember to free()
it, otherwise a leak occurs. I would recommend that approach in this case because guessing the maximum length of the string before the call might be difficult.
所以,类似的东西.假设你开始
http://cffi.readthedocs.io/en/latest/embedding.html , 改变
plugin.h
包含::
So, something like that. Assuming you start with
http://cffi.readthedocs.io/en/latest/embedding.html, change
plugin.h
to contain::
// return type is "char *"
extern char *generate_cool_page(char url[]);
并更改plugin_build.py
::
ffibuilder.embedding_init_code("""
from my_plugin import ffi, lib
@ffi.def_extern()
def generate_cool_page(url):
url = ffi.string(url)
# do some processing
return lib.strdup(str(soup)) # calls malloc()
""")
ffibuilder.cdef("""
#include <string.h>
char *strdup(const char *);
""")
从C代码中,您根本不需要initialize_api()
新的嵌入模式;相反,您只说#include "plugin.h"
并直接调用该函数:
From the C code, you don't need initialize_api()
at all in the
new embedding mode; instead, you just say #include "plugin.h"
and call the function directly::
char *data = generate_cool_page("https://example.com");
if (data == NULL) { handle_errors... }
printf("Got this: '%s'\n", data);
free(data); // important!
这篇关于如何使用cffi嵌入在C语言中返回字符串的Python函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!