如何使用C++扩展python? - c++

多亏了this handy skeleton module,我已经用C成功扩展了python。但是我找不到用于C++的程序,尝试修复C++在编译此骨架模块时给出的错误时遇到了循环依赖问题。

如何使用C++扩展Python?

如果不需要,我宁愿不依赖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)
>>> 

参考方案

首先,即使您不想引入其他依赖关系,我也建议您看一下PyCXX。引用其网页:

CXX / Objects是一组C++工具,可简化编写Python扩展的过程。 PyCXX使编写Python扩展更容易的主要方式是,它大大增加了程序不会产生引用计数错误并且不必不断检查来自Python C API的错误返回的可能性。 CXX / Objects通过以下方式将Python与C++集成:

  • 依靠C++异常处理来检测错误和清除。在复杂的函数中,这在用C编写时通常是一个巨大的问题。使用PyCXX,我们可以让编译器跟踪发生错误时需要取消引用的对象。
  • 标准模板库(STL)及其许多算法可与Python容器(如列表和元组)即插即用。
  • 可选的CXX / Extensions工具允许您将笨拙的C表替换为定义模块和扩展对象的对象和方法调用。
  • 我认为PyCXX是在BSD许可下获得许可的,这意味着如果您的扩展将以类似的许可发布,那么您也可以在扩展的分布式tarball中包含PyCXX的整个源代码。
    如果您确实绝对不想依赖PyCXX或任何其他第三方库,我认为您只需要wrap函数,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);
    }
    

    在Java中,执行“ ++++++++”表达式,编译器未报告任何错误并且可以正确执行? - java

    我用eclipse编写了这段代码,用war写过,结果为3d。public static void main(String[] args) { double a = 5d + + + + + +-+3d; System.out.println(a); } 参考方案 您的表情可以改写为(5d) + (+ + + + +-+3d) 其中第一个+是应用于两个操作数的…

    如何用Cython保持C++类名不变? - c++

    我有一个名为Foo的C++类。如果遵循Cython C++ tutorial,则需要以其他方式调用Python类,例如PyFoo。但是我确实也需要调用Python类Foo。如何有效地做到这一点?编辑:我正在尝试接口以前与Boost Python接口的现有C++库。由于不同的原因,我想测试Cython。由于使用Boost:Python调用了Python类,因此…

    SWIG和C++共享库 - c++

    我有一个C++库(我们将其称为mylib)编译为libmylib.so中的/usr/local/lib文件,并且我在my lib中一个名为/usr/local/include的目录中有许多头文件。现在,我想做的(对于初学者而言)只是将其中一个头文件(包含有关我的图书馆提供的类的信息)与SWIG一起使用,以生成mylib_wrap.cxx文件,然后进行编译并将…

    编写虚拟网络摄像头? - c++

    我需要创建一个虚拟的网络摄像头,该虚拟的网络摄像头冒充一个网络摄像头,但是将其播放的一组图像作为输入。我见过诸如ManyCam和Fake Webcam之类的解决方案,但它们似乎都在一个限制或另一个限制(分辨率,最大文件大小,fps等)上,我正在使用Windows XP SP3。我知道我必须为此任务编写WIA接口,但是作为Python程序员,我从未编写过驱动程…

    在Python和C++之间传输数据而无需写入Windows和Unix文件 - python

    我有预先存在的python和C ++文件,其中python文件定义了许多点,而C ++代码利用其现有库进行了所需的计算。最终产品是C ++代码写入的文件。我正在寻找一种在python中获取2000点列表的方法,将其传递给函数,然后执行所有C ++代码并输出我需要的文件。其他注意事项。这必须是可以在Linux或Windows机器上工作的东西,并且最少安装新插件…