[英]How can I embed a Python function that returns a string in C using cffi?
I'm trying to embed a Python function in C using PyPy and cffi. 我正在尝试使用PyPy和cffi将Python函数嵌入C中。 I'm following this guide from the PyPy documentation.
我正在遵循PyPy文档中的本指南 。
The problem is, all the examples I've found operate on ints, and my function takes a string and returns a string. 问题是,我发现的所有示例都在ints上操作,并且我的函数采用字符串并返回字符串。 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.
我似乎无法弄清楚如何将此函数嵌入C中,因为C似乎并没有真正的字符串,而是使用char数组。
Here's what I've tried: 这是我尝试过的:
# 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;
}
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: 当我运行
gcc -I/opt/pypy3/include -Wno-write-strings c_tests.c -L/opt/pypy3/bin -lpypy3-c -g -o c_tests
然后运行./c_tests
,出现此错误:
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!
I don't have a ton of experience with C and I feel like I'm misrepresenting the string argument/return value. 我没有使用C的大量经验,感觉好像我在错误地表示字符串参数/返回值。 How do I do this properly?
如何正确执行此操作?
Thanks for your help! 谢谢你的帮助!
Note that you should not be using pypy's deprecated interface to embedding; 请注意,您不应使用pypy弃用的界面进行嵌入; instead, see http://cffi.readthedocs.io/en/latest/embedding.html .
相反,请参见http://cffi.readthedocs.io/en/latest/embedding.html 。
The C language doesn't have "strings", but only arrays of chars. C语言没有“字符串”,而只有字符数组。 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; 在C语言中,要返回“字符串”的函数通常用不同的方式写成:它接受指向预先存在的缓冲区(类型为
char[]
)的指针作为第一个参数,并将该缓冲区的长度作为第二个参数; and when called, it fills the buffer. 并在调用时填充缓冲区。 This can be messy because you ideally need to handle buffer-too-small situations in the caller, eg allocate a bigger array and call the function again.
这可能很麻烦,因为理想情况下,您需要在调用程序中处理缓冲区太小的情况,例如分配更大的数组并再次调用该函数。
Alternatively, some functions give up and return a freshly malloc()
-ed char *
. 或者,某些函数放弃并返回一个新的
malloc()
ed char *
。 Then the caller must remember to free()
it, otherwise a leak occurs. 然后,调用者必须记住将其
free()
,否则会发生泄漏。 I would recommend that approach in this case because guessing the maximum length of the string before the call might be difficult. 在这种情况下,我建议使用这种方法,因为在调用之前猜测字符串的最大长度可能很困难。
So, something like that. 所以,类似的东西。 Assuming you start with http://cffi.readthedocs.io/en/latest/embedding.html , change
plugin.h
to contain:: 假设您从http://cffi.readthedocs.io/en/latest/embedding.html开始,请将
plugin.h
更改为包含:
// return type is "char *"
extern char *generate_cool_page(char url[]);
And change this bit of plugin_build.py
:: 并更改
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 *);
""")
From the C code, you don't need initialize_api()
at all in the new embedding mode; 在C代码中,在新的嵌入模式下根本不需要
initialize_api()
。 instead, you just say #include "plugin.h"
and call the function directly:: 而是说
#include "plugin.h"
并直接调用该函数:
char *data = generate_cool_page("https://example.com");
if (data == NULL) { handle_errors... }
printf("Got this: '%s'\n", data);
free(data); // important!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.