2012-11-23 24 views
5

CアプリケーションでイベントベースのPythonライブラリを使用したいと思います。 http://docs.python.org/2/c-api/index.html#c-api-indexCでのPythonの埋め込み

Cからメソッドを呼び出して戻り値を収集することは問題ありません。しかし、私は次のようにする方法がわかりません:

いくつかのPythonライブラリ関数は、引数としてPythonの関数ポインタと思われるものを取ります。

これらのメソッドをCから呼び出すときに、C関数ポインタを渡して、Python関数がコールバックとしてC関数を使用することは可能ですか?

もしそうでなければ、PythonでCコールバックを使用する方法はありますか?

+0

http://stackoverflow.com/questions/6626167/build-a-pyobject-from-a-c-function(マージする必要があります、モッズポイント外)を参照してください。 – ecatmur

+1

@ecatmurリンクされた回答が間違っています。 '' PyCFunction'は与えられた 'PyMethodDef'へのポインタを保持し、後でそれを使ってCの関数とフラグを検索するので、' methd'は静的に確保されなければなりません。答えのコードは 'PyMethodDef'を自動的に割り当てます。これは、結果のPython関数が呼び出されたときにクラッシュを引き起こします。 – user4815162342

+0

@ user4815162342:リンクされた回答では、Manuxはプロセスの概要を示していたと思います。ちょっとばかげて、IMOというのは、Manuxは関数名を単に 'NULL'ではなくモジュール名として使うということです。実際のコードでは、 'PyMethodDef'がヒープに割り当てられていると仮定します。 – eryksun

答えて

9

これは予想以上に困難ですが、実行することができます。

あなたがコールバックとして提供したい単一のC関数を持っている場合は、呼び出し可能なPythonのに変換するPyCFunction_Newを使用することができます:あなたが選択したい場合は

#include <python.h> 

static PyObject *my_callback(PyObject *ignore, PyObject *args) 
{ 
    /* ... */ 
} 

static struct PyMethodDef callback_descr = { 
    "function_name", 
    (PyCFunction) my_callback, 
    METH_VARARGS,     /* or METH_O, METH_NOARGS, etc. */ 
    NULL 
}; 

static PyObject *py_callback; 

... 
py_callback = PyCFunction_New(&callback_descr, NULL); 

このアプローチは機能しません実行時に異なるコールバック、例えばCコールバック関数をPythonコールバックに変換する汎用のc_to_python関数を提供します。その場合は、独自のtp_callの拡張タイプを実装する必要があります。

typedef struct { 
    PyObject_HEAD 
    static PyObject (*callback)(PyObject *, PyObject *); 
} CallbackObject; 

static PyObject * 
callback_call(CallbackObject *self, PyObject *args, PyObject *kwds) 
{ 
    return self->callback(args, kwds); 
} 

static PyTypeObject CallbackType = { 
    PyObject_HEAD_INIT(NULL) 
    0,       /*ob_size*/ 
    "Callback",     /*tp_name*/ 
    sizeof(CallbackObject),  /*tp_basicsize*/ 
    0,       /*tp_itemsize*/ 
    0,       /*tp_dealloc*/ 
    0,       /*tp_print*/ 
    0,       /*tp_getattr*/ 
    0,       /*tp_setattr*/ 
    0,       /*tp_compare*/ 
    0,       /*tp_repr*/ 
    0,       /*tp_as_number*/ 
    0,       /*tp_as_sequence*/ 
    0,       /*tp_as_mapping*/ 
    0,       /*tp_hash */ 
    (ternaryfunc) callback_call, /*tp_call*/ 
    0,       /*tp_str*/ 
    0,       /*tp_getattro*/ 
    0,       /*tp_setattro*/ 
    0,       /*tp_as_buffer*/ 
    Py_TPFLAGS_DEFAULT,   /*tp_flags*/ 
}; 

PyObject * 
c_to_python(PyObject (*callback)(PyObject *, PyObject *)) 
{ 
    CallbackObject *pycallback = PyObject_New(CallbackObject, &CallbackType); 
    if (pycallback) 
    pycallback->callback = callback; 
    return pycallback; 
} 

このコードは自明もuser_dataポインタを受け入れるように拡張されます。ユーザーデータをCallbackObject構造体に格納するだけです。

+0

クイックルックを持って、それはboost :: pythonがCの関数をラップするのが好きなようです。 – neodelphi

+0

@neodelphiそれは意味がありますが、質問はCでタグ付けされているので、 'boost :: python'は実際には適用されません。 BTWは 'boost :: python'を積極的に維持していますか?チェックしたところ、ドキュメントはかなり古いようでした。 – user4815162342

+0

あなたは正しいです。私は長年更新されていないので、私はライブラリのようなboost :: pythonを実装しようとしているので、この記事を見つけました。私は他の解決策を見つけられませんでした。私はboost :: pythonがこのようにしているのを見ています。これはやり方であるようです。 – neodelphi