5

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

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

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

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

そうでない場合、Python で C コールバックを使用する方法を教えてください。

4

1 に答える 1

9

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

コールバックとして提供したい C 関数が 1 つある場合は、 を使用PyCFunction_Newして Python 呼び出し可能関数に変換できます。

#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_to_python、C コールバック関数を 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構造体に保存するだけです。

于 2012-11-23T23:35:04.460 に答える