How do you extend python with C++?

前端 未结 3 1230
生来不讨喜
生来不讨喜 2021-02-02 13:31

I\'ve successfully extended python with C, thanks to this handy skeleton module. But I can\'t find one for C++, and I have circular dependency trouble when trying to fix the err

相关标签:
3条回答
  • 2021-02-02 13:51

    use extern C to wrap all the function names that get called from python. Because C++ compilers use something called 'name mangling' (necessary for dealing with overloading), python can't read c++ libraries. But extern C will solve your problems. Do it like this:

    // 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() {}
    }
    

    Make extra sure you put every function python needs inside the extern block. You can still use c++ features inside the functions, it's just that the names will be exported without 'name mangling'.

    0 讨论(0)
  • 2021-02-02 13:57

    What about Boost::Python?

    EDIT: sorry, I did oversee that you don't want to depend on boost but I think it might still be one of the best options.

    0 讨论(0)
  • 2021-02-02 14:09

    First of all, even though you don't want to introduce an additional dependency, I suggest you to have a look at PyCXX. Quoting its webpage:

    CXX/Objects is a set of C++ facilities to make it easier to write Python extensions. The chief way in which PyCXX makes it easier to write Python extensions is that it greatly increases the probability that your program will not make a reference-counting error and will not have to continually check error returns from the Python C API. CXX/Objects integrates Python with C++ in these ways:

    • C++ exception handling is relied on to detect errors and clean up. In a complicated function this is often a tremendous problem when writing in C. With PyCXX, we let the compiler keep track of what objects need to be dereferenced when an error occurs.
    • The Standard Template Library (STL) and its many algorithms plug and play with Python containers such as lists and tuples.
    • The optional CXX/Extensions facility allows you to replace the clumsy C tables with objects and method calls that define your modules and extension objects.

    I think PyCXX is licensed under the BSD license, which means that you can just as well include the whole source code of PyCXX in the distributed tarball of your extension if your extension will be released under a similar license.

    If you really and absolutely don't want to depend on PyCXX or any other third-party library, I think you only have to wrap functions that will be called by the Python interpreter in extern "C" { and } to avoid name mangling.

    Here's the corrected code:

    #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);
    }
    
    0 讨论(0)
提交回复
热议问题