I need to call some cpp functions recently with python as a large part of my data analysis code is written in pure python. Cython is the first solution coming to my mind when I realized my need. Though its initial motivation is to allow users to write a python-c mixed style language which take advantages of python's syntax and c language's performance, Cython also allow users to leverage the pure c/cpp library and port to make them accessible to python.

File structure

Consider a very simple case, I need to port several methods to python, so I declared those functions in a methods.h file and define them in methods.cpp. Then I created a .pyd file that includes the methods.h file. The pyd file is like the header file in cpp/c. Once this is finished, I created another .pyx file, which uses the cimport command to load definition from previously created .pyd file. Actually, in my project, we have more than 10 .h files and even more .hpps. This is fine as they can be all processed in the same manner, as the cythonize function can deal with a list of extensions (we will talk about this more in the next section).

Building settings

As suggested by the official doc, I use setup.py to help build the whole thing. Below is an example from the official doc:

from setuptools import setup
from Cython.Build import cythonize

setup(
    name='Hello world app',
    ext_modules=cythonize("hello.pyx"),
    zip_safe=False,
)

We can see that the hello.pyx should be our previously mentioned .pyx. But just replacing that won't work in our case as we're writing cpp. Some modern cpp feature are not enabled by default in python's distutils lib. I found that this doc pointed out a way to do it easily. Actually, you just need to change the environment variable CFLAGS by setting os.environ["CFLAGS"]. Do this before calling setup() in setup.py and everything will be fine.

Be care about data types

Since C/Cpp strictly ask for parameter type for each function, in .pyd and .pyx files you need to be specifically careful about the parameter type, for example, the std::string should then be converted to from libcpp.string cimport string. Similarly, vector also can be used with from libcpp.vector cimport vector as said in this question.