As a developer writing high-performance C++ libraries, I often find myself needing to integrate them into Python for easier scripting and flexibility. Given the speed and efficiency of C++, it’s crucial for me to maintain that performance while making the functionality accessible from Python.
I frequently turn to pybind11 for this task. It’s a lightweight solution that, once set up, is simple to use and doesn’t add unnecessary overhead.
I want to share some of the tips and tricks I’ve picked up along the way, and show how you can leverage pybind11 to bridge the gap between C++ and Python in your own projects.
C/C++ Function
Calling a function through pybind11 is straightforward. Once your function is defined, especially if marked with extern "C" for compatibility, the binding is simple. Here’s an example of how to expose a function to Python using pybind11:
In this case, the function add is a simple C++ function that takes two arguments and adds them together. By using pybind11::def, the function becomes callable directly from Python. The pybind11::arg declarations allow you to specify default arguments, making it even easier to call from Python. Once bound, the function behaves as a regular Python function while keeping the performance benefits of the underlying C++ implementation.
C++ Class
Calling a C++ class from Python is also quite straightforward when using pybind11. If your class has public members and methods, they can be easily exposed to Python. Here’s an example of how to bind a class with a function to Python:
In this example, CppClass is exposed to Python with its constructor and the class_function method. Once bound, you can create an instance of CppClass and call its methods directly from Python. This allows you to seamlessly interact with your C++ logic within Python while keeping the full speed of C++. I will cover more complex topics like handling std::vector later.
PyBind11 Class
pybind11 offers the ability to define C++ classes that can manipulate Python objects, for example, exposing numpy.array memory that can be overwritten in C++. However, since numpy arrays and std::vector are not directly compatible, it’s necessary to copy data back and forth between them. Below is an example where I define a class in C++ that inherits from CppClass and handles numpy.array manipulation.
In this example, the PyClass class inherits from CppClass and exposes the numbers and UpdateVectorNumpy functions to Python. The numbers function returns a memoryview from a C++ array, and the UpdateVectorNumpy function allows modifying a numpy.array directly from C++, reflecting the changes back into the Python side.
STL Containers Manipulation
One common issue when working with std::vector in C++ and numpy.array in Python is the memory overhead caused by copying data back and forth between the two. When it’s necessary to return a Python object from C++ without wasting memory, you can define a direct binding to the C++ std::vector<double>. This allows you to allocate the vector directly from Python, manipulate it in C++, and avoid unnecessary data copies.
Below is an example of how to bind std::vector<double> and expose it to Python:
With this setup, you can now create and manipulate a std::vector<double> directly from Python:
By directly binding std::vector<double>, you avoid the memory overhead of copying data between numpy and std::vector, and you can safely manipulate the data from both Python and C++.
Complete Example
A full example to show the different way of interfacing Python with C/C++ libraries.