C コードから Python コードを呼び出すにはどうすればよいでしょうか?
-
20-08-2019 - |
質問
大規模な C プロジェクトをいくつかの新しい機能で拡張したいと考えていますが、実際には Python で作成したいと考えています。基本的に、C コードから Python コードを呼び出したいと考えています。ただし、SWIG のような Python->C ラッパーでは、逆のこと、つまり C モジュールを作成して Python から C を呼び出すことができます。
IPC または RPC を使用するアプローチを検討しています (複数のプロセスがあっても構いません)。つまり、純粋な Python コンポーネントを別のプロセス (同じマシン上) で実行し、C プロジェクトがソケット (または Unix パイプ) への書き込み/読み取りによってコンポーネントと通信するようにします。私のPythonコンポーネントは通信するためにソケットに読み取り/書き込みできます。それは合理的なアプローチでしょうか?もっと良いものはありますか?特別な RPC メカニズムのようなものですか?
これまでの回答ありがとうございます - ただし、Python プログラムを C プログラムとして別のプロセスに置きたいため、IPC ベースのアプローチに焦点を当てたいと思います。Python インタープリターを埋め込みたくありません。ありがとう!
解決
私がお勧めするのは、 アプローチの詳細はこちら. 。まず、Python コードの文字列を実行する方法を説明し、そこから、C プログラムと対話するための Python 環境をセットアップする方法、C コードから Python 関数を呼び出す方法、C コードから Python オブジェクトを操作する方法などを詳しく説明します。
編集:本当に IPC の道を歩みたい場合は、次を使用するとよいでしょう。 構造体モジュール あるいはさらに良いことに、 プロトライブラリ. 。Python と C プロセス間の通信のほとんどは、構造体の受け渡しを中心に展開します。 ソケット上で または経由して 共有メモリ.
を作成することをお勧めします Command
コマンドとその引数を表すフィールドとコードを含む構造体。あなたが何を達成したいのかを詳しく知らないと、これ以上具体的なアドバイスはできませんが、一般的には、 プロトライブラリ ライブラリは、C プログラムと Python プログラム間の通信に使用するものであるため (免責事項:私は protlib の作者です)。
他のヒント
マニュアルの該当する章を参照してください: http://docs.python.org/extending/
基本的に、あなたのプログラムにPythonインタプリタを埋め込む必要があります。
あなただけのシェルスクリプトでPythonアプリケーションをラップして、Cアプリケーション内でからそれを呼び出すと考えていますか?
は、最もエレガントな解決策が、それは非常に簡単です。
私は、Python用IPCのアプローチを使用していない< - > Cの通信が、それはかなりうまく動作するはずです。私は、標準的なフォークのexecを行うCプログラムを持っており、通信のための子プロセスにリダイレクトさstdin
とstdout
を使用します。素敵なテキストベースの通信は、Pythonプログラムを開発し、テストすることが非常に簡単になります。
IPC を選択することに決めた場合、おそらく次のように散財するでしょう。 XML-RPC -- クロスプラットフォーム。必要に応じて、後で Python サーバー プロジェクトを別のノードに簡単に配置できます。多くの優れた実装があります (「 ここ C や Python を含む多くの場合、 ここ Python 標準ライブラリの一部である単純な XML-RPC サーバーの場合 -- 他のアプローチほど拡張性は高くありませんが、ユースケースにはおそらく問題なく便利です)。
すべてのケースに対応できる完璧な IPC アプローチではないかもしれません (あるいは、絶対に完璧な RPC アプローチですらあります!) が、利便性、柔軟性、堅牢性、実装範囲の広さは、多くの小さな欠点を上回ると私の意見ではあります。
どうやらPythonはそれが問題を解決する、のWin32 DLLにコンパイルできるようにする必要があります。
のWin32 DLLへのC#コードを変換する任意の開発ツール
によってそれが使用可能になるようにこのとてもいい http://thrift.apache.org/ のようで、程度にも本がありますこれます。
詳細ます:
スケーラブルなクロス言語のApacheのスリフト・ソフトウェア・フレームワーク、 サービスの開発、コード生成とソフトウェアスタックを組み合わせました 間で効率的かつシームレスに連携サービスを構築するためのエンジン C ++やJava、PythonやPHP、Rubyの、アーラン、Perlの、ハスケル、C#、ココア、 JavaScriptやNode.jsの、Smalltalkの、OCamlのとDelphiおよび他の言語ます。
私は<のhref = "https://docs.python.org/2/extending/embedding.html#embedding-python-in-another-application" のrel = "nofollowをの "標準" アプローチを使用しました別のアプリケーションの中に「> Pythonの埋め込み。しかし、それは退屈な/複雑です。 Pythonでの各新機能は実装が痛いです。
私はの例を見て>。これは、インタフェースを簡素化するためにCFFIを使用していますが、それはPyPy、ないのPythonが必要です。少なくとも高いレベルで、最初にこの例を読み、理解します。
私は、Pythonで動作するC / PyPyの変形例。ここでCFFIを使用してCからのPythonを呼び出す方法です。
私は1つの代わりにPythonで3つの機能を実装しているため、私の例では、より複雑です。私は前後にデータを渡すの追加の側面をカバーしたかったです。
複雑な部分は、現在のPythonにapi
のアドレスを渡すに隔離されています。ことは、一度だけ実装する必要があります。その後、それは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'
$