如何从 C 代码调用 Python 代码?
-
20-08-2019 - |
题
我想用一些新功能扩展一个大型 C 项目,但我真的想用 Python 编写它。基本上,我想从 C 代码调用 Python 代码。然而,像 SWIG 这样的 Python->C 包装器允许相反的情况,即编写 C 模块并从 Python 调用 C。
我正在考虑一种涉及 IPC 或 RPC 的方法(我不介意有多个进程);也就是说,让我的纯 Python 组件在单独的进程(在同一台机器上)中运行,并让我的 C 项目通过从套接字(或 unix 管道)写入/读取来与其通信。我的 python 组件可以读/写套接字进行通信。这是一个合理的做法吗?还有更好的吗?比如一些特殊的RPC机制?
感谢到目前为止的回答 - 但是,我想专注于基于 IPC 的方法,因为我希望将我的 Python 程序作为我的 C 程序放在单独的进程中。我不想嵌入 Python 解释器。谢谢!
解决方案
我建议 方法的详细这里.它开始由说明如何执行串的代码,然后从那里详细说明如何设立了一个Python环境与你的C节目,呼叫蟒蛇职能从你的C码,操纵蟒蛇的对象,从你C码,等等。
编辑:如果你真的想要走的路线IPC,然后你会想要使用 该模块结构 或者更好的是, protlib.最之间的通信Python和C程围绕着通过结构来回,要么 在一个插座 或者通过 共享存储器.
我建议创建一个 Command
结构与领域和代码表示命令和他们的论点。我不能给出更多具体的建议不知道更多关于什么你想要完成,但总的来说,我的建议 protlib 库,因为它是什么我使用之间的通信C和Python程序(免责声明:我是作者的protlib).
其他提示
请参阅说明书中的相关章节: http://docs.python.org/extending/
从本质上讲,你必须嵌入Python解释到你的程序。
你有没有考虑只是包装一下你的Python应用程序在shell脚本,并且从与你的C应用程序调用呢?
不是最完美的解决方案,但它是非常简单的。
我没有使用过的Python的IPC方法< - > I2C通信,但它应该工作得很好。我会在C程序做一个标准的叉Exec和使用重定向stdin
和stdout
子进程的通信。一个漂亮的基于文本的通信将使它很容易开发和测试的Python程序。
显然的Python需要能够编译到的Win32 DLL,它会解决这个问题
在这样一种方式,C#代码转换成的win32的DLL将使它可用通过任何开发工具
这似乎很不错 http://thrift.apache.org/, 甚至有本关于它。
详细信息:
Apache节俭软件框架,用于扩展的跨语言 服务开发,结合了一个软件组与一个代码 引擎建设服务的工作效率和之间的无缝 C++、Java、Python,PHP、红宝石、爱尔兰、Perl、哈斯克尔,C#、可可、 JavaScript,Node.js,一般,OCaml和德尔菲和其他语言。
我使用了“标准”方法 将 Python 嵌入到另一个应用程序中. 。但这很复杂/乏味。Python 中的每个新函数实现起来都很痛苦。
我看到一个例子 从 C 调用 PyPy. 。它使用 CFFI 来简化界面,但需要 PyPy,而不是 Python。首先至少在较高层次上阅读并理解这个示例。
我修改了 C/PyPy 示例以使用 Python。以下是如何使用 CFFI 从 C 调用 Python。
我的示例更复杂,因为我在 Python 中实现了三个函数,而不是一个。我想介绍来回传递数据的其他方面。
复杂的部分现在被隔离为传递地址 api
到Python。只需实施一次。之后,在 Python 中添加新函数就很容易了。
接口.h
// These are the three functions that I implemented in Python.
// Any additional function would be added here.
struct API {
double (*add_numbers)(double x, double y);
char* (*dump_buffer)(char *buffer, int buffer_size);
int (*release_object)(char *obj);
};
测试_cffi.c
//
// Calling Python from C.
// Based on Calling PyPy from C:
// http://doc.pypy.org/en/latest/embedding.html#more-complete-example
//
#include <stdio.h>
#include <assert.h>
#include "Python.h"
#include "interface.h"
struct API api; /* global var */
int main(int argc, char *argv[])
{
int rc;
// Start Python interpreter and initialize "api" in interface.py using
// old style "Embedding Python in Another Application":
// https://docs.python.org/2/extending/embedding.html#embedding-python-in-another-application
PyObject *pName, *pModule, *py_results;
PyObject *fill_api;
#define PYVERIFY(exp) if ((exp) == 0) { fprintf(stderr, "%s[%d]: ", __FILE__, __LINE__); PyErr_Print(); exit(1); }
Py_SetProgramName(argv[0]); /* optional but recommended */
Py_Initialize();
PyRun_SimpleString(
"import sys;"
"sys.path.insert(0, '.')" );
PYVERIFY( pName = PyString_FromString("interface") )
PYVERIFY( pModule = PyImport_Import(pName) )
Py_DECREF(pName);
PYVERIFY( fill_api = PyObject_GetAttrString(pModule, "fill_api") )
// "k" = [unsigned long],
// see https://docs.python.org/2/c-api/arg.html#c.Py_BuildValue
PYVERIFY( py_results = PyObject_CallFunction(fill_api, "k", &api) )
assert(py_results == Py_None);
// Call Python function from C using cffi.
printf("sum: %f\n", api.add_numbers(12.3, 45.6));
// More complex example.
char buffer[20];
char * result = api.dump_buffer(buffer, sizeof buffer);
assert(result != 0);
printf("buffer: %s\n", result);
// Let Python perform garbage collection on result now.
rc = api.release_object(result);
assert(rc == 0);
// Close Python interpreter.
Py_Finalize();
return 0;
}
接口.py
import cffi
import sys
import traceback
ffi = cffi.FFI()
ffi.cdef(file('interface.h').read())
# Hold references to objects to prevent garbage collection.
noGCDict = {}
# Add two numbers.
# This function was copied from the PyPy example.
@ffi.callback("double (double, double)")
def add_numbers(x, y):
return x + y
# Convert input buffer to repr(buffer).
@ffi.callback("char *(char*, int)")
def dump_buffer(buffer, buffer_len):
try:
# First attempt to access data in buffer.
# Using the ffi/lib objects:
# http://cffi.readthedocs.org/en/latest/using.html#using-the-ffi-lib-objects
# One char at time, Looks inefficient.
#data = ''.join([buffer[i] for i in xrange(buffer_len)])
# Second attempt.
# FFI Interface:
# http://cffi.readthedocs.org/en/latest/using.html#ffi-interface
# Works but doc says "str() gives inconsistent results".
#data = str( ffi.buffer(buffer, buffer_len) )
# Convert C buffer to Python str.
# Doc says [:] is recommended instead of str().
data = ffi.buffer(buffer, buffer_len)[:]
# The goal is to return repr(data)
# but it has to be converted to a C buffer.
result = ffi.new('char []', repr(data))
# Save reference to data so it's not freed until released by C program.
noGCDict[ffi.addressof(result)] = result
return result
except:
print >>sys.stderr, traceback.format_exc()
return ffi.NULL
# Release object so that Python can reclaim the memory.
@ffi.callback("int (char*)")
def release_object(ptr):
try:
del noGCDict[ptr]
return 0
except:
print >>sys.stderr, traceback.format_exc()
return 1
def fill_api(ptr):
global api
api = ffi.cast("struct API*", ptr)
api.add_numbers = add_numbers
api.dump_buffer = dump_buffer
api.release_object = release_object
编译:
gcc -o test_cffi test_cffi.c -I/home/jmudd/pgsql-native/Python-2.7.10.install/include/python2.7 -L/home/jmudd/pgsql-native/Python-2.7.10.install/lib -lpython2.7
执行:
$ test_cffi
sum: 57.900000
buffer: 'T\x9e\x04\x08\xa8\x93\xff\xbf]\x86\x04\x08\x00\x00\x00\x00\x00\x00\x00\x00'
$