2009-06-28 29 views
32

大きなCプロジェクトをいくつかの新しい機能で拡張したいのですが、実際にはPythonで記述したいと思っています。基本的には、PythonコードをCコードから呼び出す必要があります。しかし、SWIGのようなPython-> CラッパーはOPPOSITEを許しています。これはCモジュールを書いてPythonからCを呼び出すことです。CコードからPythonコードをどのように呼び出すのですか?

私はIPCまたはRPCに関連したアプローチを検討しています(私は複数のプロセスがあることに気付きません)。すなわち、私の純粋なPythonコンポーネントを別のプロセス(同じマシン上)で実行し、Cプロジェクトがソケット(またはunixパイプ)からの書き込み/読み取りによってそれと通信するようにします。私のPythonコンポーネントは通信するためにソケットに読み書きすることができます。それは合理的なアプローチですか?何か良いことがありますか?特別なRPCメカニズムのように?

これまでの回答 - しかし、私はPythonプログラムを私のCプログラムとは別のプロセスに入れたいので、IPCベースのアプローチに焦点を当てたいと思います。私はPythonインタプリタを埋め込みたくない。ありがとう!

+5

別のプロセスでPythonプログラムを入れたい、とPythonインタプリタを埋め込むために望んでいないため、あなたの根拠は何ですか?私は興味がある。 –

+1

もしPythonに文字列をパイプするだけで、それが終わったらC言語に戻る方法があれば、Pythonインタプリタを埋め込むよりはるかに簡単です。インターフェイスがシンプルで(文字列と文字列を渡すだけで)、別のPythonアプリケーションを呼び出すだけで、5分以上の時間がかかります。 – hhafez

+0

完全な例https://スタックオーバーフロー。com/a/46441794/5842403 Cで埋め込まれたPythonと、DPIを使用してSystemverilogに埋め込まれたCを見ることができます。 – Joniale

答えて

10

私はapproaches detailed hereをお勧めします。まず、Pythonコードの文字列を実行する方法を説明し、そこからCプログラムと対話するPython環境を設定する方法、CコードからPython関数を呼び出す方法、CコードからPythonオブジェクトを操作する方法などを紹介します。

EDIT:実際にIPCのルートに行く場合は、the struct module以上、さらにはprotlibを使用します。 PythonとCプロセスの間のほとんどの通信は、構造体を前後に渡すことを中心に行われ、over a socketまたはshared memoryのいずれかです。

コマンドとその引数を表すフィールドとコードを持つCommand構造体を作成することをお勧めします。あなたが達成したいことについてもっと知らなくても、私はもっと具体的な助言をすることはできませんが、一般的にはprotlibライブラリをお勧めします。これはCとPythonプログラムの間で通信するためです(免責事項:私はprotlibの著者です) 。

+1

私はこれを前にやっているし、うまくいきました。私はいくつかのCプロセスをソケット上で構造体を送ることによって通信し、Pythonプロセスも許可したいと思っていました。プロトコルのことを書くことはPythonでは簡単ではなく、ビルドの一部として実行されるPythonスクリプトを書くことができました。このファイルは、.Hファイルを解析してC構造体をパック/アンパックするためのPythonコードを自動作成します。唯一の問題はすべての文字列のパッキング/アンパックによるものです。パフォーマンスは、ネームCがmemcopyという構造体のバイナリ表現に直接作用し、生のデータをソケットからキャストすることとほとんど同じではありません。 – bdk

4

は、マニュアルの関連する章を参照してください:http://docs.python.org/extending/

基本的に、あなたのプログラムにPythonインタプリタを埋め込む必要があります。

4

Pythonアプリケーションをシェルスクリプトでラップし、Cアプリケーションでwithを呼び出すことを検討しましたか?

最も洗練されたソリューションではありませんが、非常に簡単です。

+0

実際にPythonを別のプロセスで実行し、stdin/stdioでそれを話したければ、それはちょうど最善の解決策です。 – Crashworks

1

私はPython < - > C通信にIPCアプローチを使用していませんが、かなりうまくいくはずです。私は、Cプログラムに標準のfork-execを行い、通信の子プロセスにstdinstdoutをリダイレクトして使用させます。素敵なテキストベースのコミュニケーションは、Pythonプログラムの開発とテストを非常に簡単にします。

+0

Windowsではfork-execが可能ですか?質問者のプラットフォームが何であるかは不明です。 –

+0

Windows上でfork-execを行う方法は(ほとんど)文書化されていませんが、私はそこではしません。彼はUNIXソケットを言いましたので、私はターゲットがWindowsではないと仮定しています。私はそれがWindowsの下で産卵とパイプのようになるだろうと思う。 –

1

私がIPCに参加することに決めたら、おそらくXML-RPC - クロスプラットフォームで盛んで、後でPythonサーバープロジェクトを別のノードに簡単に置くことができます。多くの優れた実装があります(here Python標準ライブラリの一部である単純なXML-RPCサーバーのためにはC言語やPythonのものを含め多くの場合、hereがあります。他のアプローチほどスケーラビリティではありません。

すべてのケース(または完全なRPCの場合でも、完璧なIPCアプローチではないかもしれません!)、利便性、柔軟性、堅牢性、実装の幅広さは、私の意見では軽微な欠陥よりも重要です。

0

が明らかにPythonのは、Win32のDLLにコンパイルできるようにする必要があり、それは、Win32のDLLにC#のコードを変換すると、これはそう

+1

これはwin32プラットフォームにのみ適用されます:-p Unixはどうですか? – Pharaun

0

任意の開発ツールによって、それが使えるようになりますように問題

を解決しますかなり良いhttp://thrift.apache.org/、それについての本もあります。

詳細:

Apacheのスリフト・ソフトウェア・フレームワークは、スケーラブルなクロス言語 サービスの開発のために、 C++の間で効率的かつシームレスに連携サービスを構築するためのコード生成 エンジンとソフトウェアスタックを組み合わせ、ジャワPython、PHP、Ruby、Erlang、Perl、Haskell、C#、Cocoa、 JavaScript、Node.js、Smalltalk、OCaml、Delphiおよびその他の言語。

0

Embedding Python in Another Applicationの「標準」アプローチを使用しました。しかし、それは複雑で面倒です。 Pythonの新しい関数はそれぞれ実装するのに苦労します。

Calling PyPy from Cの例を見ました。これはCFFIを使用してインターフェイスを簡素化しますが、PythonではなくPyPyが必要です。この例を最初に読んで理解してください。

Pythonで動作するようにC/PyPyの例を変更しました。 CFFIを使ってCからPythonを呼び出す方法は次のとおりです。

私の例は、Pythonで3つの関数を1つではなく実装したので、もっと複雑です。私は、データを前後に渡すという追加の側面をカバーしたいと思っていました。

複雑な部分は今やapiのアドレスをPythonに渡すことにまで隔離されています。それは一度だけ実装する必要があります。その後、Pythonで新しい関数を追加するのは簡単です。

コンパイル

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 

interface.py

// 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.h:

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' 
$ 
関連する問題