If you have a relatively small amount of C/C++ code to wrap, you can do it by hand. The Extending and Embedding section of the docs is a pretty good reference.
When I write wrappers for C and C++ code, I usually provide a procedural interface to the code and then use Python to construct an object-oriented interface. I do things this way for two reasons: first, exposing C++ objects to Python is a pain; and second, I prefer writing higher-level structures in Python to writing them in C++.
Let’s take a look at a basic wrapper: we have a function ‘hello’ in a file ‘hello.c’. ‘hello’ is defined like so:
char * hello(char * what)
To wrap this manually, we need to do the following.
First, write a Python-callable function that takes in a string and returns a string.
static PyObject * hello_wrapper(PyObject * self, PyObject * args)
{
char * input;
char * result;
PyObject * ret;
// parse arguments
if (!PyArg_ParseTuple(args, "s", &input)) {
return NULL;
}
// run the actual function
result = hello(input);
// build the resulting string into a Python object.
ret = PyString_FromString(result);
free(result);
return ret;
}
Second, register this function within a module’s symbol table (all Python functions live in a module, even if they’re actually C functions!)
static PyMethodDef HelloMethods[] = {
{ "hello", hello_wrapper, METH_VARARGS, "Say hello" },
{ NULL, NULL, 0, NULL }
};
Third, write an init function for the module (all extension modules require an init function).
DL_EXPORT(void) inithello(void)
{
Py_InitModule("hello", HelloMethods);
}
Fourth, write a setup.py script:
from distutils.core import setup, Extension
# the c++ extension module
extension_mod = Extension("hello", ["hellomodule.c", "hello.c"])
setup(name = "hello", ext_modules=[extension_mod])
There are two aspects of this code that are worth discussing, even at this simple level.
First, error handling: note the PyArg_ParseTuple call. That call is what tells Python that the ‘hello’ wrapper function takes precisely one argument, a string (“s” means “string”; “ss” would mean “two strings”; “si” would mean “string and integer”). The convention in the C API to Python is that a NULL return from a function that returns PyObject* indicates an error has occurred; in this case, the error information is set within PyArg_ParseTuple and we’re just passing the error on up the stack by returning NULL.
Second, references. Python works on a system of reference counting: each time a function “takes ownership” of an object (by, for example, assigning it to a list, or a dictionary) it increments that object’s reference count by one using Py_INCREF. When the object is removed from use in that particular place (e.g. removed from the list or dictionary), the reference count is decremented with Py_DECREF. When the reference count reaches 0, Python knows that this object is not being used by anything and can be freed (it may not be freed immediately, however).
Why does this matter? Well, we’re creating a PyObject in this code, with PyString_FromString. Do we need to INCREF it? To find out, go take a look at the documentation for PyString_FromString:
http://docs.python.org/api/stringObjects.html#l2h-461
See where it says “New reference”? That means it’s handing back an object with a reference count of 1, and that’s what we want. If it had said “Borrowed reference”, then we would need to INCREF the object before returning it, to indicate that we wanted the allocated memory to survive past the end of the function.
Here’s a way to think about references:
- if you receive a Python object from the Python API, you can use it within your own C code without INCREFing it.
- if you want to guarantee that the Python object survives past the end of your own C code, you must INCREF it.
- if you received an object from Python code and it was a new reference, but you don’t want it to survive past the end of your own C code, you should DECREF it.
If you wanted to return None, by the way, you can use Py_None. Remember to INCREF it!
Another note: during the class, I talked about using PyCObjects to pass opaque C/C++ data types around. This is useful if you are using Python to organize your code, but you have complex structures that you don’t need to be Python-accessible. You can wrap pointers in PyCObjects (with an associated destructor, if so desired) at which point they become opaque Python objects whose memory is managed by the Python interpreter. You can see an example in the example code, under code/hello/hellmodule.c, functions cobj_in, cobj_out, and free_my_struct, which pass an allocated C structure back to Python using a PyCObject wrapper.
So that’s a brief introduction to how you wrap things by hand.
As you might guess, however, there are a number of projects devoted to automatically wrapping code. Here’s a brief introduction to some of them.