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 errors that C++ gives when I compile this skeleton module.
How do you extend Python with C++?
I'd rather not depend on Boost (or SWIP or other libraries) if I don't have to. Dependencies are a pain in the butt. Best case scenario, I find a skeleton file that already compiles with C++.
Here's the edited skeleton I've made for 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);
}
This compiles fine for me, but when I test it:
$ 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)
>>>
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:
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:
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.
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:
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'.