Prerequisite
- Python 2.7
- python-dev (debian package, can be installed by apt-get in Ubuntu)
- GCC/G++
Hello World
1. Sources
This time we have two more C++ files: ‘hello_world.hpp’ and ‘hello_world.cpp’. They contain the C++ function that you want to wrap for Python:
hello_world.hpp:
void hello_world();
hello_world.cpp:
#include <iostream>
#include <hello_world.hpp>
using namespace std;
void hello_world(){
cout << "hello world!" << endl;
}
So here comes our new wrapper and setup script:
hello_world_wrapper.cpp:
#include <Python.h>
#include <hello_world.hpp>
static PyObject* hello_world_wrapper(PyObject * self, PyObject * args){
hello_world();
Py_RETURN_NONE;
}
PyMODINIT_FUNC inithello_world_module(void) {
static PyMethodDef method_list[] = {
{ "hello_world", (PyCFunction)hello_world_wrapper, METH_NOARGS, NULL},
{ NULL, NULL, 0, NULL } // end of methods
};
Py_InitModule3("hello_world_module", method_list, NULL);
}
setup.py:
from distutils.core import setup, Extension
extension_mod = Extension(name="hello_world_module",
sources=["hello_world_wrapper.cpp", "hello_world.cpp"],
include_dirs=["."]
)
setup(ext_modules=[extension_mod])
2. Usage
Same trick to generate hello_world.so:
python setup.py build_ext --inplace
Try to call hello_world() in a Python script:
main.py:
import hello_world_module
hello_world_module.hello_world()
3. About hello_world_wrapper.cpp
We need to do 3 things in this file:
- Wrap existing functions
Everything in Python is an object, so we define a function that takes and returns pointer of PyObject. Because the wrapper function itself is also an object, so we need to have an extra ‘self’ pointer as the first argument. Inside the wrapper function, hello_world() is called and then the macroPy_RETURN_NONE
is used to return a None pointer to Python. Returning NULL to the python/c API indicates that an error has occurred and you may see the below error since an exception hasn’t been set:SystemError: error return without exception set
- Write a Method Mapping Table
A Method Mapping Table can be created with an array of PyMethodDef. According to the official doc, PyMethodDef consists of four fields:- ml_name: the name of the method that can be used in Python.
- ml_meth: the entry of a C function that will be executed when the method is called in Python. In our case, when hello_world() is called in Python, hello_world_wrapper() gets executed.
- ml_flags: specify what kind of inputs are acceptable for this method, available options are METH_NOARGS, METH_VARARGS, METH_KEYWORDS, and METH_VARARGS|METH_KEYWORDS. In our example, METH_NOARGS tells Python that the method hello_world() takes no arguments. The Method Mapping Table must end with {NULL, NULL, 0, NULL}.
- ml_doc: the docstring for the function, which could be NULL if you do not feel like writing one
- Write a module init function
Basically the same as the previous example except that the Method Mapping Table is passed into Py_InitModule3().
4. About setup.py
This time we use another parameter to initialise Extention: include_dirs=["."]
, this simply tells Python to add the current directory to compiler include path so that #include <hello_world.hpp>
is legal.
References
[1] Python Extension Programming with C (http://www.tutorialspoint.com/python/python_further_extensions.htm)