如何使用 cffi 在 C 语言中嵌入一个 Python 函数 returns 一个字符串?
How can I embed a Python function that returns a string in C using cffi?
我正在尝试使用 PyPy 和 cffi 在 C 中嵌入一个 Python 函数。我正在关注 PyPy 文档中的 this guide。
问题是,我发现的所有示例都对整数进行操作,而我的函数接受一个字符串,returns 接受一个字符串。我似乎无法弄清楚如何在 C 中嵌入此函数,因为 C 似乎并没有真正的字符串,而是使用字符数组。
这是我试过的方法:
# 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
时,我得到这个错误:
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 没有太多的经验,我觉得我在错误地表达字符串 argument/return 的值。我该如何正确执行此操作?
感谢您的帮助!
请注意,您不应使用 pypy 已弃用的接口进行嵌入;相反,请参阅 http://cffi.readthedocs.io/en/latest/embedding.html.
C 语言没有"strings",只有字符数组。在 C 中,一个想要 return a "string" 的函数通常写成
不同的是:它接受一个指向预先存在的缓冲区(char[]
类型)的指针作为第一个参数,第二个参数是该缓冲区的长度;当被调用时,它会填充缓冲区。这可能会很麻烦,因为理想情况下您需要处理调用者中缓冲区太小的情况,例如分配一个更大的数组并再次调用该函数。
或者,一些函数放弃并 return 一个新的 malloc()
-ed char *
。那么调用者一定要记得free()
它,否则会发生泄漏。在这种情况下,我会推荐这种方法,因为在调用之前猜测字符串的最大长度可能很困难。
所以,类似的东西。假设你开始
http://cffi.readthedocs.io/en/latest/embedding.html, 改变
plugin.h
包含::
// 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"
并直接调用函数::
char *data = generate_cool_page("https://example.com");
if (data == NULL) { handle_errors... }
printf("Got this: '%s'\n", data);
free(data); // important!
我正在尝试使用 PyPy 和 cffi 在 C 中嵌入一个 Python 函数。我正在关注 PyPy 文档中的 this guide。
问题是,我发现的所有示例都对整数进行操作,而我的函数接受一个字符串,returns 接受一个字符串。我似乎无法弄清楚如何在 C 中嵌入此函数,因为 C 似乎并没有真正的字符串,而是使用字符数组。
这是我试过的方法:
# 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
时,我得到这个错误:
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 没有太多的经验,我觉得我在错误地表达字符串 argument/return 的值。我该如何正确执行此操作?
感谢您的帮助!
请注意,您不应使用 pypy 已弃用的接口进行嵌入;相反,请参阅 http://cffi.readthedocs.io/en/latest/embedding.html.
C 语言没有"strings",只有字符数组。在 C 中,一个想要 return a "string" 的函数通常写成
不同的是:它接受一个指向预先存在的缓冲区(char[]
类型)的指针作为第一个参数,第二个参数是该缓冲区的长度;当被调用时,它会填充缓冲区。这可能会很麻烦,因为理想情况下您需要处理调用者中缓冲区太小的情况,例如分配一个更大的数组并再次调用该函数。
或者,一些函数放弃并 return 一个新的 malloc()
-ed char *
。那么调用者一定要记得free()
它,否则会发生泄漏。在这种情况下,我会推荐这种方法,因为在调用之前猜测字符串的最大长度可能很困难。
所以,类似的东西。假设你开始
http://cffi.readthedocs.io/en/latest/embedding.html, 改变
plugin.h
包含::
// 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"
并直接调用函数::
char *data = generate_cool_page("https://example.com");
if (data == NULL) { handle_errors... }
printf("Got this: '%s'\n", data);
free(data); // important!