Calling C++ from Python
The Problem
Say you’ve written a library in C++ and you want to call it from Python. You basically have three options:
Below I try to give you a flavor for each.
SWIG
Given an interface file specifying the headers you wish to wrap plus some
wrapping directives, SWIG will generate a single source file containing an
extern "C"
API for all relevant classes. It will also generate a Python
module (e.g. mylib.py
) that creates an object-oriented overlay on top of
the C API. In this approach the Python API will largely mirror the C++ API,
though you do have the ability to add new methods as part of the wrapping
process. Depending on the complexity of the code you are wrapping you may
need to fiddle with the interface file, for example to instantiate templates
and catch exceptions.
Next, you compile the wrapper file and link the resulting object file together
with the library being wrapped as well as any external dependencies into a
single shared library named _mylib.so
. In order for this to work you must
add Python headers such as pyconfig.h
to your include path and the python
interpreter (e.g. libpython2.7
) to your library path. You will also need
to make sure all object code is position-independent, which might mean
recompiling externals with -fPIC
.
Boost.Python
Instead of generating the wrappers for you based on an interface file, Boost.Python effectively gives you a template-based DSL for manually specifying how to map C++ classes onto Python classes. Although this provides very fine-grained control over the Python API, allowing you to e.g. turn getters into properties, it does mean that each class has to be wrapped individually.
Once you are done crafting the wrapper code you compile and link it the same
as with SWIG, except that in this case there is no *.py
file and you directly
import mylib.so
in python.
Cython
Cython is similar to SWIG in that it generates wrappers for you, but instead of
a single interface file you have a *.pxd
file and a *.pyx
file which are
both written in Cython (a kind of extended Python). The
examples
I found
online tended
to leave various details out, so I put together a working toy example, found
below. It assumes that Anaconda is installed in
$ANACONDA_DIR
and that you touch include/wrappers.hpp
.
include/Adder.hpp
:
src/Adder.cpp
:
include/Multiplier.hpp
:
src/Multiplier.cpp
:
wrappers.pxd
:
wrappers.pyx
:
Makefile
: