Skip to content

Interfacing with Cpp

Christopher Aicher edited this page Oct 4, 2016 · 9 revisions

Using Cython to connect Cpp to Python

First, install cython using conda (from within the project's virtual environment):

$ conda install cython

To build cython extensions for a python project, we must add the extension to setup.py and call

$ python setup.py build_ext --inplace            # build cpp extension

This compiles and builds the cpp code and returns a dynamic library that can be linked to from python.

Most of the work is done in the .pyx cython wrapper files.

To link a C++ library to python using cython, a wrapper .pyx file is necessary. The Cython wrapper is very similar to python, except objects can be statically typed using the cdef command (as oppose to python's dynamically duck-typed at runtime).

See cython userguide for more details.

Import notes:

  • The first two lines of a .pyx file are important and must specify the language and .cpp source files. For example
# distutils: language = c++
# distutils: sources = cython/src/<name>.cpp
  • The setup.py file includes the Cython extensions using the setuptools.Extension class. To get the dynamically linked libraries (.so files) to be in the appropriate folder use a dot . in the name. For example
extensions = [
    Extension(name = "path.to.output.name", ... ), # Creates 'path/to/output/name.so'
    ... ] 

Building Cpp

The C++ code is stored in the cython folder. The header and source files are in cython/src. The unit tests are in cython/test.

To build (stand-alone) C++ files/executables, we use CMake. Recall CMake is a structured language that helps organize and generate Makefiles. Then by calling make, the Makefiles compile the code.

Because CMake generates a lot of junk, the workflow is to build the project in a build directory

$ cd [...]/cython
$ mkdir build
$ cd build
$ cmake ..  # Setups up the Makefiles in the `build` directory
$ make      # Compiles the code in the `build` directory

To clean up the project, just remove the build folder.

Adding new C++ sources, headers, and tests, requires modifying the appropriate CMakeLists.txt files.

Cpp Unit Test

The unit tests are written using the CXX_TEST framework and are added to the CMakeLists within the build directory (read up on CMake's FindCxxTest module for additional details). Don't forget to link the relevant libraries from the src directory.

To run the tests, build the project and call make test or use the command ctest (both from the build folder). I prefer to call ctest --verbose from the build folder.

Don't forget you can run tests individually via the cxx_test executables (compiled in the build\test folder).

Debugging Cpp with gdb

To debug Cpp code we must build our libraries in 'debug' mode.

$ cd [...]/cython
$ mkdir debug # folder to build in 'debug' mode.
$ cd debug
$ cmake -D CMAKE_BUILD_TYPE=Debug ..  # Setups up the Makefiles in the `build` directory
$ make      # Compiles the code in the `debug` directory

By selecting the CMAKE_BUILD_TYPE=Debug, we build the cpp files with the g++ -g -O0 flags, which allow for debugging with gdb.

To debug an executable file (e.g. a CXXTest my_tester) call $ gdb test/my_tester. This will begin a gdb session.

Connecting C++ with Numpy using Eigen

We connect python's numpy with C++ eigen we use Eigency.

Eigen

First install eigen by downloading the most recent copy and using cmake to install it to /usr/local/include/eigen3. Read the Install guide in the archived tar file. For convenience, add syslinks from eigen3 to include

$ cd /usr/local/include
$ sudo ln -sf eigen3/Eigen Eigen

For more documentation on how to use eigen, read the docs link and quickreference guide link

Eigency

First install eigency by cloning the repo and calling python setup.py install (make sure cython is installed). Then read the README.md.

Be aware that Eigen follows column-major format and Python/C++ follows row-major format, so transpose appropriately link. The simplest solution is to make sure np.ndarray are in column-major format using the np.asfortranarray or np.array(..., order = 'F') options.

Don't forget to import eigency in the .pyx file by calling from eigency.core cimport *. The cimport is important.