我想扩展一个带有一些新功能的大型C项目,但我真的想用Python编写它。基本上,我想从C代码调用Python代码。但是,像SWIG这样的Python-> C包装器允许OPPOSITE,即编写C模块并从Python调用C语言。
我正在考虑一种涉及IPC或RPC的方法(我不介意有多个进程);也就是说,让我的纯Python组件在一个单独的进程中运行(在同一台机器上),并让我的C项目通过从套接字(或unix管道)写入/读取来与之通信。我的python组件可以读/写socket来进行通信。这是一种合理的方法吗?还有更好的东西吗?像一些特殊的RPC机制?
感谢到目前为止的答案 - 然而,我想专注于基于IPC的方法,因为我希望将我的Python程序作为我的C程序在一个单独的过程中。我不想嵌入Python解释器。谢谢!
答案 0 :(得分:11)
我推荐approaches detailed here。首先介绍如何执行Python代码字符串,然后详细介绍如何设置Python环境以与C程序交互,从C代码调用Python函数,从C代码处理Python对象等等。
编辑:如果你真的想走IPC的路线,那么你会想要使用the struct module或更好,protlib。 Python和C进程之间的大多数通信都围绕来回传递结构,over a socket或shared memory。
我建议使用字段和代码创建一个Command
结构来表示命令及其参数。我不能在不了解你想要完成什么的情况下提供更具体的建议,但总的来说我推荐protlib库,因为它是我用来在C和Python程序之间进行通信的(免责声明:我是protlib的作者。
答案 1 :(得分:4)
请参阅手册中的相关章节:http://docs.python.org/extending/
基本上你必须将python解释器嵌入你的程序中。
答案 2 :(得分:4)
您是否考虑过将python应用程序包装在shell脚本中并在C应用程序中调用它?
不是最优雅的解决方案,但它非常简单。
答案 3 :(得分:1)
我没有使用IPC方法进行Python< - > C通信,但它应该可以很好地工作。我希望C程序执行标准的fork-exec,并在子进程中使用重定向的stdin
和stdout
进行通信。一个很好的基于文本的通信将使开发和测试Python程序变得非常容易。
答案 4 :(得分:1)
如果我决定使用IPC,我可能会挥霍XML-RPC - 跨平台,如果你愿意,可以让你轻松地将Python服务器项目放在不同的节点上,有很多优秀的实现(对于许多(包括C和Python),请参阅here,对于Python标准库中的简单XML-RPC服务器,请参阅here - 不像其他方法那样具有高度可扩展性,但可能非常方便你的用例)。
对于所有情况来说,它可能不是一个完美的IPC方法(或者甚至是完美的RPC方法!),但是方便性,灵活性,稳健性和广泛的实现都超过了我的许多小缺陷。意见。
答案 5 :(得分:0)
以这种方式将c#代码转换为win32 dll将使其可用于任何开发工具
答案 6 :(得分:0)
这看起来很不错http://thrift.apache.org/,甚至还有一本关于它的书。
详细说明:
Apache Thrift软件框架,用于可扩展的跨语言 服务开发,将软件堆栈与代码生成相结合 引擎构建服务,在两者之间高效无缝地工作 C ++,Java,Python,PHP,Ruby,Erlang,Perl,Haskell,C#,Cocoa, JavaScript,Node.js,Smalltalk,OCaml和Delphi等语言。
答案 7 :(得分:0)
我使用了Embedding Python in Another Application的“标准”方法。但这很复杂/乏味。 Python中的每个新函数都很难实现。
我看到Calling PyPy from C的一个例子。它使用CFFI来简化界面,但它需要PyPy,而不是Python。首先阅读并理解这个例子,至少在高层次上。
我修改了C / PyPy示例以使用Python。以下是如何使用CFFI从C调用Python。
我的例子比较复杂,因为我用Python实现了三个函数而不是一个。我想介绍来回传递数据的其他方面。
复杂的部分现在被隔离为将api
的地址传递给Python。这只需实施一次。之后,很容易在Python中添加新功能。
interface.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);
};
test_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;
}
interface.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'
$
答案 8 :(得分:0)
与Python 3绑定的一些技巧
ffi.cdef(open('interface.h').read())
Create a PyStr from a UTF-8 encoded null-terminated character buffer.
Python 2: PyString_FromString
Python 3: PyUnicode_FromString
更改为:PYVERIFY(pName = PyUnicode_FromString(“ interface”))
wchar_t *name = Py_DecodeLocale(argv[0], NULL);
Py_SetProgramName(name);
gcc cc.c -o cc -I/usr/include/python3.6m -I/usr/include/x86_64-linux-gnu/python3.6m -lpython3.6m
def get_prediction(buffer, buffer_len):
try:
data = ffi.buffer(buffer, buffer_len)[:]
result = ffi.new('char []', data)
print('\n I am doing something here here........',data )
resultA = ffi.new('char []', b"Failed") ### New message
##noGCDict[ffi.addressof(resultA)] = resultA
return resultA
except:
print >>sys.stderr, traceback.format_exc()
return ffi.NULL
}
希望它将帮助您并节省一些时间