Skip to content

pacs-course/pybind11_examples

 
 

Repository files navigation

Contents

Introduction

The power of pybind11 is captured by the following citation from pybind11's readme:

pybind11 is a lightweight header-only library that exposes C++ types in Python and vice versa, mainly to create Python bindings of existing C++ code. Its goals and syntax are similar to the excellent Boost.Python library by David Abrahams ...

The main issue with Boost.Python - and the reason for creating such a similar project — is Boost. Boost is an enormously large and complex suite of utility libraries that works with almost every C++ compiler in existence. ... Now that C++11-compatible compilers are widely available, this heavy machinery has become an excessively large and unnecessary dependency.

This repository contains several examples for the usage of pybind11. Even though the online documentation provided by the developers of pybind11 makes the usage of it relatively straightforward, several examples - such as provided here - make pybind11 even easier to use. These examples are meant for you to start quicker with pybind11. They are, however, by no means exhaustive, and do not always provide the optimal choice. Therefore it is highly advisable to think for yourself. Furthermore, contributions with similar simple examples (or by further improving existing examples) are highly welcome. Please file a pull request or an issue on GitHub, or contact me.

To give credit where credit is due:

Note that there are also test cases that double as examples in the pybind11 repository, but these are not very insightful when you are new to pybind11.

Finally, pybind11 is actively used. So one can look in actively maintained libraries for specific solutions. For example:

  • cppmat is a library that provided multidimensional arrays in C++, much like the Eigen library does for one- and two-dimensional arrays. It also provides a pybind11 interface, such that creating a Python module that uses cppmat objects as (return) arguments in the back end functions becomes trivial.

Cloning this repository

The pybind11 module (which is header only!) is included as a submodule of this repository. This requires some attention when cloning this project. There are two options:

  • The simplest option is:

    git clone --recursive https://github.com/tdegeus/pybind11_examples.git

    This will download the submodule up to the version that is used in this project. To update to the latest commit of the submodule itself:

    git submodule update --remote
  • One could also directly download the submodule from the source:

    git clone https://github.com/tdegeus/pybind11_examples.git
    cd pybind11_examples
    git submodule init
    git submodule update

Dependencies

The Eigen library is used in some of the NumPy examples. From these examples it can be observed that through pybind11, Eigen and NumPy are really handshake modules. Almost no code is needed to create the C++/Python interface. Note that most of the simplicity depends on copies being passed, some attention is needed if one wants to pass purely by reference.

Eigen does not need installation because it is also header only. One just needs to download the files and include the headers at compile time.

In general one can download and install Eigen by:

mkdir /path/to/temp/build
cmake /path/to/eigen/download
make install

For macOS one could simply use

brew install eigen

Thereafter compile with

clang++ -I/path/to/eigen/instalation ...

Note that, when properly configured (which is normally the case), pkg-config can be used to keep track of the paths:

clang++ `pkg-config --cflags eigen3` ...

Or one can use CMake (see below).

Compiling strategies

DIY

If you have a simple library you might want to do everything yourself. In this case you compile your C++ source to a shared object which is linked to Python. This boils down to

c++ -O3 -shared -std=gnu++11 -I ./pybind11/include `python3-config --cflags --ldflags --libs` example.cpp -o example.so -fPIC

CMake

Basic usage

pybind11 applications can be compiled very easy using CMake. For simplicity pybind11 is included as a sub-folder of the examples below (in fact using a symbolic link to over many copies), so that we can use a CMakeLists.txt like:

cmake_minimum_required(VERSION 2.8.12)
project(example)

add_subdirectory(pybind11)
pybind11_add_module(example example.cpp)

(whereby this example module consists of a single source file example.cpp). To compile it, use:

cd /path/to/build
cmake /path/to/example/src
make

Using Eigen

When Eigen is 'installed' it can be easily included by adding the following in CMakeLists.txt:

find_package( PkgConfig )
pkg_check_modules( EIGEN3 REQUIRED eigen3 )
include_directories( ${EIGEN3_INCLUDE_DIRS} )

Using the C++14 standard

The C++14 standard can be used by including the following in CMakeLists.txt:

set(CMAKE_CXX_STANDARD 14)

setup.py

A file setup.py can be added to your library. You can then compile and install using

python3 setup.py build
python3 setup.py install

Although writing the setup.py is not hard it is not covered here. One can use some tools included in cppmat, that can be installed with pip (e.g. pip3 install cppmat). Furthermore on can look at the setup.py of for example GooseTensor or several other of my repositories.

Examples

This example features one function modify that takes a list (read-only), multiplies all entries by two, and returns it as a list of doubles (see example.cpp). From Python this function is contained in a simple module example (see test.py).

The purpose of this example is to show how to make a function accept a list, how to convert this to the standard C++ std::vector, and how to return a new std::vector (or list). Note that the actual operation is not very important, it is the interface that is illustrated.

To compile, either employ CMake, whereby the compilation instructions are read from CMakeLists.txt by subsequently:

cmake .
make

Or, compile directly using:

c++ -O3 -shared -std=gnu++11 -I ./pybind11/include `python3-config --cflags --ldflags --libs` example.cpp -o example.so -fPIC

Run the example by:

python3 test.py

To run with Python 2, simply replace the two occurrences of "python3" above with "python". To modify the CMake instructions find more online.

Same as the previous example, but with a nested list.

A function modify that converts the entries from a one-dimensional array to integers, and then multiplies these entries by 10.

The purpose of this example is to show how to make a function accept a one-dimensional NumPy array, how to convert this to the standard C++ std::vector, and how to return a one-dimensional NumPy array. Note that the interface generated using pybind11 is so flexible that it even accepts list inputs on the Python side.

One function length. This function accepts a 'matrix' in which comprises a list of 2-D position vectors, as rows. The result is again a 'matrix' with for each row the "x" and "y" position, and the length of the 2-D position vector.

Two functions det and inv that use the Eigen library.

The purpose of this example is to show how trivial the interaction between C++/Eigen and Python/NumPy is.

To compile using CMake and to run, follow the instructions above (whereby the Eigen headers are included in CMakeLists.txt. To compile directly, additionally the Eigen headers have to be included:

c++ -O3 -shared -std=gnu++11 -I /path/to/eigen -I ./pybind11/include `python3-config --cflags --ldflags --libs` example.cpp -o example.so -fPIC

For example on macOS with homebrew:

c++ -O3 -shared -std=gnu++11 -I /usr/local/Cellar/eigen/3.3.1/include/eigen3 -I ./pybind11/include `python3-config --cflags --ldflags --libs` example.cpp -o example.so -fPIC

A custom CustomVectorXd class with one function mul. This class uses the Eigen library. It also includes a default argument.

Furthermore, this example has a function trans (totally unrelated to the custom CustomVectorXd class). It's purpose is to show how to return a new Eigen::VectorXi (or NumPy-array).

One overloaded function mul. This function acts 'differently' if it is called with int arguments or double arguments. Notice that the default behavior of pybind11 is quite robust. When calling the function with one int and one double argument, the module will choose the double version of mul (and will cast the int argument to a double).

To compile, make sure that the C++14 standard is used, for example by including -std=c++14 as compiler argument.

Similar to the previous example, but with Eigen arguments (i.e. NumPy arguments from the Python side).

To compile, make sure that the C++14 standard is used.

This example includes a custom matrix class in C++ (in matrix.h). This class is coupled to a NumPy-array using a simple interface (in pybind_matrix.h). Consequently the functions (in example.cpp) do not necessitate any special wrapper code.

See also this discussion of Stack Overflow.

This example features a way to interface with an enumerator in C++. In principle the interface is straightforward but warrants a 'trick'. Here a submodule is used to be able to interact with the enumerator in the same way as in C++.

This example contains a classical example where one or more classes are derived from a certain parent or template. This particular example contains two animals, a Dog and a Cat, that are both derived from a generic Animal class. There is a function talk that accepts the generic Animal and thus any of the derived classes.

This particular case requires more involved interface, as is described in the documentation.

This example features a simple CRTP with as 'base' and and a 'derived' class, and its registration to the pybind11 API.

Sometimes py::overload_cast is not able to resolve your function, for example when the return type cannot be inferred. In that case you can be explicit by static_casting a pointer to your function More information can be found in the documentation.

About

Examples for the usage of "pybind11"

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C++ 73.5%
  • CMake 18.4%
  • Python 8.1%