Как вы расширяете Python с C ++? - PullRequest
18 голосов
/ 17 мая 2010

Я успешно расширил python с помощью C, благодаря этому удобному скелетному модулю . Но я не могу найти его для C ++, и у меня возникают проблемы с циклической зависимостью при попытке исправить ошибки, которые выдает C ++ при компиляции этого каркасного модуля.

Как вы расширяете Python с C ++?

Я бы предпочел не зависеть от Boost (или SWIP или других библиотек), если бы мне это не нужно. Зависимости - это боль в заднице. В лучшем случае я нахожу файл скелета, который уже компилируется с C ++.

Вот отредактированный скелет, который я сделал для C ++:

#include <Python.h>

#include "Flp.h"

static PyObject * ErrorObject;

typedef struct {
    PyObject_HEAD
    PyObject * x_attr; // attributes dictionary
} FlpObject;

static void Flp_dealloc(FlpObject * self);
static PyObject * Flp_getattr(FlpObject * self, char * name);
static int Flp_setattr(FlpObject * self, char * name, PyObject * v);
DL_EXPORT(void) initflp();

static PyTypeObject Flp_Type = {
    /* The ob_type field must be initialized in the module init function
     * to be portable to Windows without using C++. */
    PyObject_HEAD_INIT(NULL)
    0,          /*ob_size*/
    "Flp",          /*tp_name*/
    sizeof(FlpObject),  /*tp_basicsize*/
    0,          /*tp_itemsize*/
    /* methods */
    (destructor)Flp_dealloc, /*tp_dealloc*/
    0,          /*tp_print*/
    (getattrfunc)Flp_getattr, /*tp_getattr*/
    (setattrfunc)Flp_setattr, /*tp_setattr*/
    0,          /*tp_compare*/
    0,          /*tp_repr*/
    0,          /*tp_as_number*/
    0,          /*tp_as_sequence*/
    0,          /*tp_as_mapping*/
    0,          /*tp_hash*/
};

#define FlpObject_Check(v) ((v)->ob_type == &Flp_Type)

static FlpObject * newFlpObject(PyObject * arg)
{
    FlpObject * self;
    self = PyObject_NEW(FlpObject, &Flp_Type);
    if (self == NULL)
        return NULL;
    self->x_attr = NULL;
    return self;
}

// Flp methods

static void Flp_dealloc(FlpObject * self)
{
    Py_XDECREF(self->x_attr);
    PyMem_DEL(self);
}

static PyObject * Flp_demo(FlpObject * self, PyObject * args)
{
    if (! PyArg_ParseTuple(args, ""))
        return NULL;
    Py_INCREF(Py_None);
    return Py_None;
}

static PyMethodDef Flp_methods[] = {
    {"demo",    (PyCFunction)Flp_demo,  1},
    {NULL,      NULL} // sentinel
};

static PyObject * Flp_getattr(FlpObject * self, char * name)
{
    if (self->x_attr != NULL) {
        PyObject * v = PyDict_GetItemString(self->x_attr, name);
        if (v != NULL) {
            Py_INCREF(v);
            return v;
        }
    }
    return Py_FindMethod(Flp_methods, (PyObject *)self, name);
}

static int Flp_setattr(FlpObject * self, char * name, PyObject * v)
{
    if (self->x_attr == NULL) {
        self->x_attr = PyDict_New();
        if (self->x_attr == NULL)
            return -1;
    }
    if (v == NULL) {
        int rv = PyDict_DelItemString(self->x_attr, name);
        if (rv < 0)
            PyErr_SetString(PyExc_AttributeError,
                    "delete non-existing Flp attribute");
        return rv;
    }
    else
        return PyDict_SetItemString(self->x_attr, name, v);
}
/* --------------------------------------------------------------------- */

/* Function of two integers returning integer */

static PyObject * flp_foo(PyObject * self, PyObject * args)
{
    long i, j;
    long res;
    if (!PyArg_ParseTuple(args, "ll", &i, &j))
        return NULL;
    res = i+j; /* flpX Do something here */
    return PyInt_FromLong(res);
}


/* Function of no arguments returning new Flp object */

static PyObject * flp_new(PyObject * self, PyObject * args)
{
    FlpObject *rv;

    if (!PyArg_ParseTuple(args, ""))
        return NULL;
    rv = newFlpObject(args);
    if ( rv == NULL )
        return NULL;
    return (PyObject *)rv;
}

/* Example with subtle bug from extensions manual ("Thin Ice"). */

static PyObject * flp_bug(PyObject * self, PyObject * args)
{
    PyObject *list, *item;

    if (!PyArg_ParseTuple(args, "O", &list))
        return NULL;

    item = PyList_GetItem(list, 0);
    /* Py_INCREF(item); */
    PyList_SetItem(list, 1, PyInt_FromLong(0L));
    PyObject_Print(item, stdout, 0);
    printf("\n");
    /* Py_DECREF(item); */

    Py_INCREF(Py_None);
    return Py_None;
}

/* Test bad format character */

static PyObject * flp_roj(PyObject * self, PyObject * args)
{
    PyObject *a;
    long b;
    if (!PyArg_ParseTuple(args, "O#", &a, &b))
        return NULL;
    Py_INCREF(Py_None);
    return Py_None;
}


/* List of functions defined in the module */

static PyMethodDef flp_methods[] = {
    {"roj",     flp_roj,     1},
    {"foo",     flp_foo,     1},
    {"new",     flp_new,     1},
    {"bug",     flp_bug,     1},
    {NULL,      NULL}       /* sentinel */
};


/* Initialization function for the module (*must* be called initflp) */

DL_EXPORT(void) initflp()
{
    PyObject *m, *d;

    /* Initialize the type of the new type object here; doing it here
     * is required for portability to Windows without requiring C++. */
    Flp_Type.ob_type = &PyType_Type;

    /* Create the module and add the functions */
    m = Py_InitModule("flp", flp_methods);

    /* Add some symbolic constants to the module */
    d = PyModule_GetDict(m);
    ErrorObject = PyErr_NewException("flp.error", NULL, NULL);
    PyDict_SetItemString(d, "error", ErrorObject);
}

Это прекрасно для меня, но когда я проверяю это:

$ python
Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41) 
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import flp
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: dynamic module does not define init function (initflp)
>>> 

Ответы [ 3 ]

13 голосов
/ 17 мая 2010

Прежде всего, даже если вы не хотите вводить дополнительную зависимость, я предлагаю вам взглянуть на PyCXX . Цитирую свою веб-страницу:

CXX / Objects - это набор средств C ++, облегчающих написание расширений Python. Главный способ, с помощью которого PyCXX облегчает написание расширений Python, заключается в том, что он значительно увеличивает вероятность того, что ваша программа не допустит ошибки подсчета ссылок и не будет постоянно проверять возврат ошибок из API-интерфейса Python C. CXX / Objects интегрирует Python с C ++ следующими способами:

  • Обработка исключений в C ++ используется для обнаружения и устранения ошибок. В сложной функции это часто огромная проблема при написании на C. В PyCXX мы позволяем компилятору отслеживать, какие объекты необходимо разыменовать при возникновении ошибки.
  • Стандартная библиотека шаблонов (STL) и ее многочисленные алгоритмы подключаются и работают с контейнерами Python, такими как списки и кортежи.
  • Опциональное средство CXX / Extensions позволяет вам заменить неуклюжие таблицы C объектами и вызовами методов, которые определяют ваши модули и объекты расширения.

Я думаю, что PyCXX лицензируется по лицензии BSD, что означает, что вы также можете включить весь исходный код PyCXX в распределенный архив вашего расширения, если ваше расширение будет выпущено по аналогичной лицензии.

Если вы действительно и абсолютно не хотите зависеть от PyCXX или любой другой сторонней библиотеки, я думаю, вам нужно только обернуть функции, которые будут вызываться интерпретатором Python в extern "C" { и }, чтобы избежать искажения имени.

Вот исправленный код:

#include <Python.h>

#include "Flp.h"

static PyObject * ErrorObject;

typedef struct {
    PyObject_HEAD
    PyObject * x_attr; // attributes dictionary
} FlpObject;

extern "C" {
    static void Flp_dealloc(FlpObject * self);
    static PyObject * Flp_getattr(FlpObject * self, char * name);
    static int Flp_setattr(FlpObject * self, char * name, PyObject * v);
    DL_EXPORT(void) initflp();
}

static PyTypeObject Flp_Type = {
    /* The ob_type field must be initialized in the module init function
     * to be portable to Windows without using C++. */
    PyObject_HEAD_INIT(NULL)
    0,          /*ob_size*/
    "Flp",          /*tp_name*/
    sizeof(FlpObject),  /*tp_basicsize*/
    0,          /*tp_itemsize*/
    /* methods */
    (destructor)Flp_dealloc, /*tp_dealloc*/
    0,          /*tp_print*/
    (getattrfunc)Flp_getattr, /*tp_getattr*/
    (setattrfunc)Flp_setattr, /*tp_setattr*/
    0,          /*tp_compare*/
    0,          /*tp_repr*/
    0,          /*tp_as_number*/
    0,          /*tp_as_sequence*/
    0,          /*tp_as_mapping*/
    0,          /*tp_hash*/
};

#define FlpObject_Check(v) ((v)->ob_type == &Flp_Type)

static FlpObject * newFlpObject(PyObject * arg)
{
    FlpObject * self;
    self = PyObject_NEW(FlpObject, &Flp_Type);
    if (self == NULL)
        return NULL;
    self->x_attr = NULL;
    return self;
}

// Flp methods

static void Flp_dealloc(FlpObject * self)
{
    Py_XDECREF(self->x_attr);
    PyMem_DEL(self);
}

static PyObject * Flp_demo(FlpObject * self, PyObject * args)
{
    if (! PyArg_ParseTuple(args, ""))
        return NULL;
    Py_INCREF(Py_None);
    return Py_None;
}

static PyMethodDef Flp_methods[] = {
    {"demo",    (PyCFunction)Flp_demo,  1},
    {NULL,      NULL} // sentinel
};

static PyObject * Flp_getattr(FlpObject * self, char * name)
{
    if (self->x_attr != NULL) {
        PyObject * v = PyDict_GetItemString(self->x_attr, name);
        if (v != NULL) {
            Py_INCREF(v);
            return v;
        }
    }
    return Py_FindMethod(Flp_methods, (PyObject *)self, name);
}

static int Flp_setattr(FlpObject * self, char * name, PyObject * v)
{
    if (self->x_attr == NULL) {
        self->x_attr = PyDict_New();
        if (self->x_attr == NULL)
            return -1;
    }
    if (v == NULL) {
        int rv = PyDict_DelItemString(self->x_attr, name);
        if (rv < 0)
            PyErr_SetString(PyExc_AttributeError,
                    "delete non-existing Flp attribute");
        return rv;
    }
    else
        return PyDict_SetItemString(self->x_attr, name, v);
}
/* --------------------------------------------------------------------- */

/* Function of two integers returning integer */

static PyObject * flp_foo(PyObject * self, PyObject * args)
{
    long i, j;
    long res;
    if (!PyArg_ParseTuple(args, "ll", &i, &j))
        return NULL;
    res = i+j; /* flpX Do something here */
    return PyInt_FromLong(res);
}


/* Function of no arguments returning new Flp object */

static PyObject * flp_new(PyObject * self, PyObject * args)
{
    FlpObject *rv;

    if (!PyArg_ParseTuple(args, ""))
        return NULL;
    rv = newFlpObject(args);
    if ( rv == NULL )
        return NULL;
    return (PyObject *)rv;
}

/* Example with subtle bug from extensions manual ("Thin Ice"). */

static PyObject * flp_bug(PyObject * self, PyObject * args)
{
    PyObject *list, *item;

    if (!PyArg_ParseTuple(args, "O", &list))
        return NULL;

    item = PyList_GetItem(list, 0);
    /* Py_INCREF(item); */
    PyList_SetItem(list, 1, PyInt_FromLong(0L));
    PyObject_Print(item, stdout, 0);
    printf("\n");
    /* Py_DECREF(item); */

    Py_INCREF(Py_None);
    return Py_None;
}

/* Test bad format character */

static PyObject * flp_roj(PyObject * self, PyObject * args)
{
    PyObject *a;
    long b;
    if (!PyArg_ParseTuple(args, "O#", &a, &b))
        return NULL;
    Py_INCREF(Py_None);
    return Py_None;
}


/* List of functions defined in the module */

static PyMethodDef flp_methods[] = {
    {"roj",     flp_roj,     1},
    {"foo",     flp_foo,     1},
    {"new",     flp_new,     1},
    {"bug",     flp_bug,     1},
    {NULL,      NULL}       /* sentinel */
};


/* Initialization function for the module (*must* be called initflp) */

DL_EXPORT(void) initflp()
{
    PyObject *m, *d;

    /* Initialize the type of the new type object here; doing it here
     * is required for portability to Windows without requiring C++. */
    Flp_Type.ob_type = &PyType_Type;

    /* Create the module and add the functions */
    m = Py_InitModule("flp", flp_methods);

    /* Add some symbolic constants to the module */
    d = PyModule_GetDict(m);
    ErrorObject = PyErr_NewException("flp.error", NULL, NULL);
    PyDict_SetItemString(d, "error", ErrorObject);
}
5 голосов
/ 17 мая 2010

используйте extern C для переноса всех имен функций, которые вызываются из python. Поскольку компиляторы C ++ используют нечто, называемое «искажение имени» (необходимое для борьбы с перегрузкой), python не может читать библиотеки c ++. Но extern C решит ваши проблемы. Сделайте это так:

// most of your code can go whereever
void cpp_function() {}
extern "C" {
   // all functions that python calls directly must go in here
   void python_function() {}
}

Удостоверьтесь, что вы помещаете каждую нужную функцию python в блок extern. Вы по-прежнему можете использовать функции c ++ внутри функций, просто имена будут экспортироваться без «искажения имен».

1 голос
/ 17 мая 2010

А как насчет Boost :: Python ?

РЕДАКТИРОВАТЬ: извините, я наблюдал, что вы не хотите зависеть от повышения, но я думаю, что это может быть одним из лучших вариантов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...