├── .gitignore ├── .gitmodules ├── 01_py-list_cpp-vector ├── CMakeLists.txt ├── example.cpp ├── pybind11 └── test.py ├── 02_py-nested-list_cpp-nested-vector ├── CMakeLists.txt ├── example.cpp ├── pybind11 └── test.py ├── 03_numpy-1D_cpp-vector ├── CMakeLists.txt ├── example.cpp ├── pybind11 └── test.py ├── 04_numpy-2D_cpp-vector ├── CMakeLists.txt ├── example.cpp ├── pybind11 └── test.py ├── 05_numpy-2D_cpp-eigen ├── CMakeLists.txt ├── example.cpp ├── pybind11 └── test.py ├── 06_class-numpy-eigen ├── CMakeLists.txt ├── example.cpp ├── pybind11 └── test.py ├── 07_cpp-overload-scalar ├── CMakeLists.txt ├── example.cpp ├── pybind11 └── test.py ├── 08_cpp-overload-eigen ├── CMakeLists.txt ├── example.cpp ├── pybind11 └── test.py ├── 09_numpy_cpp-custom-matrix ├── CMakeLists.txt ├── example.cpp ├── matrix.h ├── pybind11 ├── pybind_matrix.h └── test.py ├── 10_enum ├── CMakeLists.txt ├── example.cpp ├── pybind11 └── test.py ├── 11_class-parent-child ├── CMakeLists.txt ├── example.cpp ├── pybind11 └── test.py ├── 12_crtp ├── CMakeLists.txt ├── example.py └── main.cpp ├── 13_static_cast ├── CMakeLists.txt ├── code_and_wrapper.cpp └── test.py ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeCache.txt 2 | CMakeFiles 3 | Makefile 4 | cmake_install.cmake 5 | 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | 11 | # C extensions 12 | *.so 13 | *.so* 14 | 15 | # Distribution / packaging 16 | .Python 17 | env/ 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *,cover 52 | .hypothesis/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | 62 | # Flask stuff: 63 | instance/ 64 | .webassets-cache 65 | 66 | # Scrapy stuff: 67 | .scrapy 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | 72 | # PyBuilder 73 | target/ 74 | 75 | # IPython Notebook 76 | .ipynb_checkpoints 77 | 78 | # pyenv 79 | .python-version 80 | 81 | # celery beat schedule file 82 | celerybeat-schedule 83 | 84 | # dotenv 85 | .env 86 | 87 | # virtualenv 88 | venv/ 89 | ENV/ 90 | 91 | # Spyder project settings 92 | .spyderproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "pybind11"] 2 | path = pybind11 3 | url = git@github.com:pybind/pybind11.git 4 | -------------------------------------------------------------------------------- /01_py-list_cpp-vector/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | project(example) 3 | 4 | add_subdirectory(pybind11) 5 | pybind11_add_module(example example.cpp) 6 | -------------------------------------------------------------------------------- /01_py-list_cpp-vector/example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // ---------------- 6 | // Regular C++ code 7 | // ---------------- 8 | 9 | // multiply all entries by 2.0 10 | // input: std::vector ([...]) (read only) 11 | // output: std::vector ([...]) (new copy) 12 | std::vector modify(const std::vector& input) 13 | { 14 | std::vector output; 15 | 16 | std::transform( 17 | input.begin(), 18 | input.end(), 19 | std::back_inserter(output), 20 | [](double x) -> double { return 2.*x; } 21 | ); 22 | 23 | // N.B. this is equivalent to (but there are also other ways to do the same) 24 | // 25 | // std::vector output(input.size()); 26 | // 27 | // for ( size_t i = 0 ; i < input.size() ; ++i ) 28 | // output[i] = 2. * input[i]; 29 | 30 | return output; 31 | } 32 | 33 | // ---------------- 34 | // Python interface 35 | // ---------------- 36 | 37 | namespace py = pybind11; 38 | 39 | PYBIND11_MODULE(example,m) 40 | { 41 | m.doc() = "pybind11 example plugin"; 42 | 43 | m.def("modify", &modify, "Multiply all entries of a list by 2.0"); 44 | } 45 | -------------------------------------------------------------------------------- /01_py-list_cpp-vector/pybind11: -------------------------------------------------------------------------------- 1 | ../pybind11 -------------------------------------------------------------------------------- /01_py-list_cpp-vector/test.py: -------------------------------------------------------------------------------- 1 | 2 | import example 3 | 4 | A = [1.,2.,3.,4.] 5 | 6 | B = example.modify(A) 7 | 8 | print(B) 9 | 10 | -------------------------------------------------------------------------------- /02_py-nested-list_cpp-nested-vector/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | project(example) 3 | 4 | add_subdirectory(pybind11) 5 | pybind11_add_module(example example.cpp) 6 | -------------------------------------------------------------------------------- /02_py-nested-list_cpp-nested-vector/example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // ---------------- 6 | // Regular C++ code 7 | // ---------------- 8 | 9 | // multiply all entries by 2.0 10 | // input: nested std::vector ([[...],[...]]) (read-only) 11 | // output: nested std::vector ([[...],[...]]) (new copy) 12 | std::vector> modify(const std::vector>& input) 13 | { 14 | std::vector> output; 15 | 16 | std::transform( 17 | input.begin(), 18 | input.end(), 19 | std::back_inserter(output), 20 | [](const std::vector &iv) { 21 | std::vector dv; 22 | std::transform(iv.begin(), iv.end(), std::back_inserter(dv), [](double x) -> double { return 2.*x; }); 23 | return dv; 24 | } 25 | ); 26 | 27 | return output; 28 | } 29 | 30 | // ---------------- 31 | // Python interface 32 | // ---------------- 33 | 34 | namespace py = pybind11; 35 | 36 | PYBIND11_MODULE(example,m) 37 | { 38 | m.doc() = "pybind11 example plugin"; 39 | 40 | m.def("modify", &modify, "Multiply all entries of a nested list by 2.0"); 41 | } 42 | -------------------------------------------------------------------------------- /02_py-nested-list_cpp-nested-vector/pybind11: -------------------------------------------------------------------------------- 1 | ../pybind11 -------------------------------------------------------------------------------- /02_py-nested-list_cpp-nested-vector/test.py: -------------------------------------------------------------------------------- 1 | 2 | import example 3 | 4 | A = [[1,2,3,4],[5,6]] 5 | 6 | B = example.modify(A) 7 | 8 | print(B) 9 | -------------------------------------------------------------------------------- /03_numpy-1D_cpp-vector/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | project(example) 3 | 4 | add_subdirectory(pybind11) 5 | pybind11_add_module(example example.cpp) 6 | -------------------------------------------------------------------------------- /03_numpy-1D_cpp-vector/example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // ------------- 7 | // pure C++ code 8 | // ------------- 9 | 10 | std::vector multiply(const std::vector& input) 11 | { 12 | std::vector output(input.size()); 13 | 14 | for ( size_t i = 0 ; i < input.size() ; ++i ) 15 | output[i] = 10*static_cast(input[i]); 16 | 17 | return output; 18 | } 19 | 20 | // ---------------- 21 | // Python interface 22 | // ---------------- 23 | 24 | namespace py = pybind11; 25 | 26 | // wrap C++ function with NumPy array IO 27 | py::array_t py_multiply(py::array_t array) 28 | { 29 | // allocate std::vector (to pass to the C++ function) 30 | std::vector array_vec(array.size()); 31 | 32 | // copy py::array -> std::vector 33 | std::memcpy(array_vec.data(),array.data(),array.size()*sizeof(double)); 34 | 35 | // call pure C++ function 36 | std::vector result_vec = multiply(array_vec); 37 | 38 | // allocate py::array (to pass the result of the C++ function to Python) 39 | auto result = py::array_t(array.size()); 40 | auto result_buffer = result.request(); 41 | int *result_ptr = (int *) result_buffer.ptr; 42 | 43 | // copy std::vector -> py::array 44 | std::memcpy(result_ptr,result_vec.data(),result_vec.size()*sizeof(int)); 45 | 46 | return result; 47 | } 48 | 49 | // wrap as Python module 50 | PYBIND11_MODULE(example,m) 51 | { 52 | m.doc() = "pybind11 example plugin"; 53 | 54 | m.def("multiply", &py_multiply, "Convert all entries of an 1-D NumPy-array to int and multiply by 10"); 55 | } 56 | -------------------------------------------------------------------------------- /03_numpy-1D_cpp-vector/pybind11: -------------------------------------------------------------------------------- 1 | ../pybind11 -------------------------------------------------------------------------------- /03_numpy-1D_cpp-vector/test.py: -------------------------------------------------------------------------------- 1 | 2 | import numpy as np 3 | import example 4 | 5 | A = [0,1,2,3,4,5] 6 | B = example.multiply(A) 7 | 8 | print('input list = ',A) 9 | print('output = ',B) 10 | 11 | A = np.arange(10) 12 | B = example.multiply(A) 13 | 14 | print('input list = ',A) 15 | print('output = ',B) 16 | 17 | 18 | -------------------------------------------------------------------------------- /04_numpy-2D_cpp-vector/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | project(example) 3 | 4 | find_package( PkgConfig ) 5 | pkg_check_modules( EIGEN3 REQUIRED eigen3 ) 6 | include_directories( ${EIGEN3_INCLUDE_DIRS} ) 7 | 8 | add_subdirectory(pybind11) 9 | pybind11_add_module(example example.cpp) 10 | -------------------------------------------------------------------------------- /04_numpy-2D_cpp-vector/example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // ------------- 7 | // pure C++ code 8 | // ------------- 9 | 10 | std::vector length(const std::vector& pos) 11 | { 12 | size_t N = pos.size() / 2; 13 | 14 | std::vector output(N*3); 15 | 16 | for ( size_t i = 0 ; i < N ; ++i ) { 17 | output[i*3+0] = pos[i*2+0]; 18 | output[i*3+1] = pos[i*2+1]; 19 | output[i*3+2] = std::pow(pos[i*2+0]*pos[i*2+1],.5); 20 | } 21 | 22 | return output; 23 | } 24 | 25 | // ---------------- 26 | // Python interface 27 | // ---------------- 28 | 29 | namespace py = pybind11; 30 | 31 | // wrap C++ function with NumPy array IO 32 | py::array py_length(py::array_t array) 33 | { 34 | // check input dimensions 35 | if ( array.ndim() != 2 ) 36 | throw std::runtime_error("Input should be 2-D NumPy array"); 37 | if ( array.shape()[1] != 2 ) 38 | throw std::runtime_error("Input should have size [N,2]"); 39 | 40 | // allocate std::vector (to pass to the C++ function) 41 | std::vector pos(array.size()); 42 | 43 | // copy py::array -> std::vector 44 | std::memcpy(pos.data(),array.data(),array.size()*sizeof(double)); 45 | 46 | // call pure C++ function 47 | std::vector result = length(pos); 48 | 49 | ssize_t ndim = 2; 50 | std::vector shape = { array.shape()[0] , 3 }; 51 | std::vector strides = { sizeof(double)*3 , sizeof(double) }; 52 | 53 | // return 2-D NumPy array 54 | return py::array(py::buffer_info( 55 | result.data(), /* data as contiguous array */ 56 | sizeof(double), /* size of one scalar */ 57 | py::format_descriptor::format(), /* data type */ 58 | ndim, /* number of dimensions */ 59 | shape, /* shape of the matrix */ 60 | strides /* strides for each axis */ 61 | )); 62 | } 63 | 64 | // wrap as Python module 65 | PYBIND11_MODULE(example,m) 66 | { 67 | m.doc() = "pybind11 example plugin"; 68 | 69 | m.def("length", &py_length, "Calculate the length of an array of vectors"); 70 | } 71 | -------------------------------------------------------------------------------- /04_numpy-2D_cpp-vector/pybind11: -------------------------------------------------------------------------------- 1 | ../pybind11 -------------------------------------------------------------------------------- /04_numpy-2D_cpp-vector/test.py: -------------------------------------------------------------------------------- 1 | 2 | import numpy as np 3 | import example 4 | 5 | A = np.arange(10).reshape(5,2) 6 | B = example.length(A) 7 | 8 | print('A = \n',A) 9 | print('B = \n',B) 10 | -------------------------------------------------------------------------------- /05_numpy-2D_cpp-eigen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | project(example) 3 | 4 | find_package( PkgConfig ) 5 | pkg_check_modules( EIGEN3 REQUIRED eigen3 ) 6 | include_directories( ${EIGEN3_INCLUDE_DIRS} ) 7 | 8 | add_subdirectory(pybind11) 9 | pybind11_add_module(example example.cpp) 10 | -------------------------------------------------------------------------------- /05_numpy-2D_cpp-eigen/example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | // N.B. this would equally work with Eigen-types that are not predefined. For example replacing 7 | // all occurrences of "Eigen::MatrixXd" with "MatD", with the following definition: 8 | // 9 | // typedef Eigen::Matrix MatD; 10 | 11 | // ---------------- 12 | // regular C++ code 13 | // ---------------- 14 | 15 | Eigen::MatrixXd inv(const Eigen::MatrixXd &xs) 16 | { 17 | return xs.inverse(); 18 | } 19 | 20 | double det(const Eigen::MatrixXd &xs) 21 | { 22 | return xs.determinant(); 23 | } 24 | 25 | // ---------------- 26 | // Python interface 27 | // ---------------- 28 | 29 | namespace py = pybind11; 30 | 31 | PYBIND11_MODULE(example,m) 32 | { 33 | m.doc() = "pybind11 example plugin"; 34 | 35 | m.def("inv", &inv); 36 | 37 | m.def("det", &det); 38 | } 39 | -------------------------------------------------------------------------------- /05_numpy-2D_cpp-eigen/pybind11: -------------------------------------------------------------------------------- 1 | ../pybind11 -------------------------------------------------------------------------------- /05_numpy-2D_cpp-eigen/test.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import example 3 | 4 | A = np.array([[1,2,1], 5 | [2,1,0], 6 | [-1,1,2]]) 7 | 8 | print('A = \n' , A) 9 | print('example.det(A) = \n' , example .det(A)) 10 | print('numpy.linalg.det(A) = \n' , np.linalg.det(A)) 11 | print('example.inv(A) = \n' , example .inv(A)) 12 | print('numpy.linalg.inv(A) = \n' , np.linalg.inv(A)) 13 | 14 | -------------------------------------------------------------------------------- /06_class-numpy-eigen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | project(example) 3 | 4 | find_package( PkgConfig ) 5 | pkg_check_modules( EIGEN3 REQUIRED eigen3 ) 6 | include_directories( ${EIGEN3_INCLUDE_DIRS} ) 7 | 8 | add_subdirectory(pybind11) 9 | pybind11_add_module(example example.cpp) 10 | -------------------------------------------------------------------------------- /06_class-numpy-eigen/example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | // ------------------ 7 | // regular C++ header 8 | // ------------------ 9 | 10 | // a custom vector-class, with one function "mul" 11 | class CustomVectorXd 12 | { 13 | private: 14 | 15 | Eigen::VectorXd m_data; 16 | 17 | public: 18 | 19 | CustomVectorXd(const Eigen::VectorXd &data); 20 | 21 | Eigen::VectorXd mul(double factor=1.); 22 | 23 | }; 24 | 25 | // ---------------- 26 | // regular C++ code 27 | // ---------------- 28 | 29 | // class-constructor: store the input "Eigen::VectorXd" 30 | CustomVectorXd::CustomVectorXd(const Eigen::VectorXd &data) : m_data(data) 31 | { 32 | } 33 | 34 | // return the custom vector, multiplied by a factor 35 | Eigen::VectorXd CustomVectorXd::mul(double factor) 36 | { 37 | return factor*m_data; 38 | } 39 | 40 | // a function that has nothing to do with the class 41 | // the point is to show how one can return a copy "Eigen::VectorXd" 42 | Eigen::VectorXi trans(const Eigen::VectorXi& array) 43 | { 44 | auto N = array.size(); 45 | 46 | Eigen::VectorXi out(N); 47 | 48 | for ( auto i = 0 ; i < N ; ++i ) 49 | out[array.size()-i-1] = array[i]; 50 | 51 | return out; 52 | } 53 | 54 | // ---------------- 55 | // Python interface 56 | // ---------------- 57 | 58 | namespace py = pybind11; 59 | 60 | PYBIND11_MODULE(example,m) 61 | { 62 | m.doc() = "pybind11 example plugin"; 63 | 64 | m.def("trans", &trans); 65 | 66 | py::class_(m, "CustomVectorXd") 67 | .def(py::init()) 68 | .def("mul", &CustomVectorXd::mul,pybind11::arg("factor")=1.) 69 | .def("__repr__", 70 | [](const CustomVectorXd &a) { 71 | return ""; 72 | } 73 | ); 74 | } 75 | -------------------------------------------------------------------------------- /06_class-numpy-eigen/pybind11: -------------------------------------------------------------------------------- 1 | ../pybind11 -------------------------------------------------------------------------------- /06_class-numpy-eigen/test.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import example 3 | 4 | A = np.arange(10) 5 | 6 | print('A = \n',A) 7 | 8 | array = example.CustomVectorXd(A) 9 | 10 | print('array.mul(default) = \n' ,array.mul() ) 11 | print('array.mul(factor=100) = \n',array.mul(factor=100)) 12 | 13 | print('trans(A) = \n',example.trans(A)) 14 | -------------------------------------------------------------------------------- /07_cpp-overload-scalar/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | project(example) 3 | 4 | set(CMAKE_CXX_STANDARD 14) 5 | 6 | find_package( PkgConfig ) 7 | pkg_check_modules( EIGEN3 REQUIRED eigen3 ) 8 | include_directories( ${EIGEN3_INCLUDE_DIRS} ) 9 | 10 | add_subdirectory(pybind11) 11 | pybind11_add_module(example example.cpp) 12 | -------------------------------------------------------------------------------- /07_cpp-overload-scalar/example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // ---------------- 5 | // regular C++ code 6 | // ---------------- 7 | 8 | double mul(double a, double b) 9 | { 10 | std::cout << "Double" << std::endl; 11 | return a*b; 12 | } 13 | 14 | int mul(int a, int b) 15 | { 16 | std::cout << "Int" << std::endl; 17 | return a*b; 18 | } 19 | 20 | // ---------------- 21 | // Python interface 22 | // ---------------- 23 | 24 | namespace py = pybind11; 25 | 26 | PYBIND11_MODULE(example,m) 27 | { 28 | m.doc() = "pybind11 example plugin"; 29 | 30 | m.def("mul", py::overload_cast(&mul) ); 31 | m.def("mul", py::overload_cast(&mul) ); 32 | } 33 | -------------------------------------------------------------------------------- /07_cpp-overload-scalar/pybind11: -------------------------------------------------------------------------------- 1 | ../pybind11 -------------------------------------------------------------------------------- /07_cpp-overload-scalar/test.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import example 3 | 4 | print(example.mul(10.,20.)) 5 | print(example.mul(10 ,20.)) 6 | print(example.mul(10 ,20 )) 7 | 8 | -------------------------------------------------------------------------------- /08_cpp-overload-eigen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | project(example) 3 | 4 | set (CMAKE_CXX_STANDARD 14) 5 | 6 | find_package( PkgConfig ) 7 | pkg_check_modules( EIGEN3 REQUIRED eigen3 ) 8 | include_directories( ${EIGEN3_INCLUDE_DIRS} ) 9 | 10 | add_subdirectory(pybind11) 11 | pybind11_add_module(example example.cpp) 12 | -------------------------------------------------------------------------------- /08_cpp-overload-eigen/example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | // ---------------- 8 | // regular C++ code 9 | // ---------------- 10 | 11 | Eigen::MatrixXd mul(const Eigen::MatrixXd &xs, double fac) 12 | { 13 | std::cout << "Double" << std::endl; 14 | return fac*xs; 15 | } 16 | 17 | Eigen::MatrixXi mul(const Eigen::MatrixXi &xs, int fac) 18 | { 19 | std::cout << "Int" << std::endl; 20 | return fac*xs; 21 | } 22 | 23 | // ---------------- 24 | // Python interface 25 | // ---------------- 26 | 27 | namespace py = pybind11; 28 | 29 | PYBIND11_MODULE(example,m) 30 | { 31 | m.doc() = "pybind11 example plugin"; 32 | 33 | // N.B. the order here is crucial, in the reversed order every "int" is converted to a "double" 34 | m.def("mul", py::overload_cast(&mul) ); 35 | m.def("mul", py::overload_cast(&mul) ); 36 | } 37 | -------------------------------------------------------------------------------- /08_cpp-overload-eigen/pybind11: -------------------------------------------------------------------------------- 1 | ../pybind11 -------------------------------------------------------------------------------- /08_cpp-overload-eigen/test.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import example 3 | 4 | A = np.array([[1,2,1], 5 | [2,1,0], 6 | [-1,1,2]]) 7 | B = 10 8 | 9 | print(example.mul(A.astype(np.int ),int (B))) 10 | print(example.mul(A.astype(np.float),float(B))) 11 | -------------------------------------------------------------------------------- /09_numpy_cpp-custom-matrix/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | project(example) 3 | 4 | set (CMAKE_CXX_STANDARD 14) 5 | 6 | add_subdirectory(pybind11) 7 | pybind11_add_module(example example.cpp) 8 | -------------------------------------------------------------------------------- /09_numpy_cpp-custom-matrix/example.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "matrix.h" 3 | #include "pybind_matrix.h" 4 | 5 | // ---------------- 6 | // regular C++ code 7 | // ---------------- 8 | 9 | Matrix mul(const Matrix &A, const Matrix &B) 10 | { 11 | if ( A.shape()!=B.shape() ) 12 | throw std::length_error("Matrix 'A' and 'B' are inconsistent"); 13 | 14 | Matrix ret(A.shape()); 15 | 16 | for ( int i=0 ; i 6 | #include 7 | #include 8 | 9 | template 10 | class Matrix 11 | { 12 | 13 | private: 14 | 15 | std::vector m_data; // data array 16 | std::vector m_shape; // number of entries in each dimensions 17 | std::vector m_strides; // stride length for each index 18 | 19 | public: 20 | 21 | // default constructor 22 | // ------------------- 23 | 24 | Matrix(){}; 25 | 26 | // constructor 27 | // ----------- 28 | 29 | Matrix(const std::vector &shape, const T *data=NULL) 30 | { 31 | if ( shape.size()<1 || shape.size()>3 ) 32 | throw std::runtime_error("Input should be 1-D, 2-D, or 3-D"); 33 | 34 | // store 'm_strides' and 'm_shape' always in 3-D, 35 | // use unit-length for "extra" dimensions (> 'shape.size()') 36 | while ( m_shape .size()<3 ) { m_shape .push_back(1); } 37 | while ( m_strides.size()<3 ) { m_strides.push_back(1); } 38 | 39 | for ( int i=0 ; i &) = default; 63 | Matrix& operator= (const Matrix &) = default; 64 | 65 | // index operators 66 | // --------------- 67 | 68 | T& operator[](size_t i) { return m_data[i]; }; 69 | const T& operator[](size_t i) const { return m_data[i]; }; 70 | 71 | T& operator()(size_t h, size_t i=0, size_t j=0) 72 | { return m_data[h*m_strides[0]+i*m_strides[1]+j*m_strides[2]]; }; 73 | 74 | const T& operator()(size_t h, size_t i=0, size_t j=0) const 75 | { return m_data[h*m_strides[0]+i*m_strides[1]+j*m_strides[2]]; }; 76 | 77 | // iterators 78 | // --------- 79 | 80 | auto begin() { return m_data.begin(); } 81 | auto begin() const { return m_data.begin(); } 82 | auto end() { return m_data.end(); } 83 | auto end() const { return m_data.end(); } 84 | 85 | // return pointer to data 86 | // ---------------------- 87 | 88 | const T* data(void) const { return m_data.data(); }; 89 | 90 | // return shape array [ndim] 91 | // ------------------------- 92 | 93 | std::vector shape(size_t ndim=0) const 94 | { 95 | if ( ndim == 0 ) 96 | ndim = this->ndim(); 97 | 98 | std::vector ret(ndim); 99 | 100 | for ( size_t i = 0 ; i < ndim ; ++i ) 101 | ret[i] = m_shape[i]; 102 | 103 | return ret; 104 | }; 105 | 106 | // return strides array [ndim] 107 | // --------------------------- 108 | 109 | std::vector strides(bool bytes=false) const 110 | { 111 | size_t ndim = this->ndim(); 112 | std::vector ret(ndim); 113 | 114 | for ( size_t i = 0 ; i < ndim ; ++i ) 115 | ret[i] = m_strides[i]; 116 | 117 | if ( bytes ) 118 | for ( size_t i = 0 ; i < ndim ; ++i ) 119 | ret[i] *= sizeof(T); 120 | 121 | return ret; 122 | }; 123 | 124 | // return size 125 | // ----------- 126 | 127 | size_t size ( void ) const { return m_data.size(); }; 128 | 129 | // return number of dimensions 130 | // --------------------------- 131 | 132 | size_t ndim ( void ) const 133 | { 134 | size_t i; 135 | 136 | for ( i = 2 ; i > 0 ; i-- ) 137 | if ( m_shape[i] != 1 ) 138 | break; 139 | 140 | return i+1; 141 | }; 142 | 143 | }; // class Matrix 144 | 145 | #endif 146 | -------------------------------------------------------------------------------- /09_numpy_cpp-custom-matrix/pybind11: -------------------------------------------------------------------------------- 1 | ../pybind11 -------------------------------------------------------------------------------- /09_numpy_cpp-custom-matrix/pybind_matrix.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "matrix.h" 7 | 8 | namespace py = pybind11; 9 | 10 | // type caster: Matrix <-> NumPy-array 11 | namespace pybind11 { namespace detail { 12 | template struct type_caster> 13 | { 14 | public: 15 | 16 | PYBIND11_TYPE_CASTER(Matrix, _("Matrix")); 17 | 18 | // Conversion part 1 (Python -> C++) 19 | bool load(py::handle src, bool convert) 20 | { 21 | if (!convert && !py::array_t::check_(src)) 22 | return false; 23 | 24 | auto buf = py::array_t::ensure(src); 25 | if (!buf) 26 | return false; 27 | 28 | auto dims = buf.ndim(); 29 | if (dims < 1 || dims > 3) 30 | return false; 31 | 32 | std::vector shape(buf.ndim()); 33 | 34 | for ( int i=0 ; i(shape,buf.data()); 38 | 39 | return true; 40 | } 41 | 42 | //Conversion part 2 (C++ -> Python) 43 | static py::handle cast(const Matrix& src, 44 | py::return_value_policy policy, py::handle parent) 45 | { 46 | py::array a(std::move(src.shape()), std::move(src.strides(true)), src.data() ); 47 | 48 | return a.release(); 49 | } 50 | }; 51 | }} 52 | -------------------------------------------------------------------------------- /09_numpy_cpp-custom-matrix/test.py: -------------------------------------------------------------------------------- 1 | 2 | import numpy as np 3 | import example 4 | 5 | A = np.arange(5) 6 | B = np.arange(5) 7 | 8 | print(example.mul(A,B)) 9 | 10 | A = np.arange(25).reshape(5,5) 11 | B = np.arange(25).reshape(5,5) 12 | 13 | print(example.mul(A,B)) 14 | 15 | A = np.arange(125).reshape(5,5,5) 16 | B = np.arange(125).reshape(5,5,5) 17 | 18 | print(example.mul(A,B)) 19 | -------------------------------------------------------------------------------- /10_enum/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | project(example) 3 | 4 | set (CMAKE_CXX_STANDARD 14) 5 | 6 | add_subdirectory(pybind11) 7 | pybind11_add_module(example example.cpp) 8 | -------------------------------------------------------------------------------- /10_enum/example.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | // ------------------ 7 | // regular C++ header 8 | // ------------------ 9 | 10 | struct Type { 11 | enum Value { 12 | Cat, 13 | Dog, 14 | }; 15 | }; 16 | 17 | // ---------------- 18 | // regular C++ code 19 | // ---------------- 20 | 21 | void whichAnimal(int animal) 22 | { 23 | if ( animal == Type::Cat ) std::cout << "Cat" << std::endl; 24 | else if ( animal == Type::Dog ) std::cout << "Dog" << std::endl; 25 | else std::cout << "Unknown" << std::endl; 26 | } 27 | 28 | // ---------------- 29 | // Python interface 30 | // ---------------- 31 | 32 | namespace py = pybind11; 33 | 34 | PYBIND11_MODULE(example, m) 35 | { 36 | py::module sm = m.def_submodule("Type", "Type enumerator"); 37 | 38 | py::enum_(sm, "Type") 39 | .value("Cat", Type::Cat) 40 | .value("Dog", Type::Dog) 41 | .export_values(); 42 | 43 | m.def("whichAnimal", &whichAnimal, py::arg("animal")); 44 | } 45 | -------------------------------------------------------------------------------- /10_enum/pybind11: -------------------------------------------------------------------------------- 1 | ../pybind11 -------------------------------------------------------------------------------- /10_enum/test.py: -------------------------------------------------------------------------------- 1 | 2 | import example 3 | 4 | print(example.Type.Cat) 5 | 6 | example.whichAnimal(example.Type.Dog) 7 | -------------------------------------------------------------------------------- /11_class-parent-child/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | project(example) 3 | 4 | set (CMAKE_CXX_STANDARD 14) 5 | 6 | add_subdirectory(pybind11) 7 | pybind11_add_module(example example.cpp) 8 | -------------------------------------------------------------------------------- /11_class-parent-child/example.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | // ------------------ 7 | // regular C++ header 8 | // ------------------ 9 | 10 | // parent class 11 | class Animal 12 | { 13 | public: 14 | virtual std::string talk(int n_times) = 0; 15 | }; 16 | 17 | // derived class 18 | class Dog : public Animal 19 | { 20 | public: 21 | std::string talk(int n_times) override; 22 | }; 23 | 24 | // derived class 25 | class Cat : public Animal 26 | { 27 | public: 28 | std::string talk(int n_times) override; 29 | }; 30 | 31 | // function that takes the parent and all derived classes 32 | std::string talk(Animal &animal, int n_times=1); 33 | 34 | // ---------------- 35 | // regular C++ code 36 | // ---------------- 37 | 38 | inline std::string Dog::talk(int n_times) 39 | { 40 | std::string result; 41 | 42 | for ( int i = 0 ; i < n_times ; ++i ) 43 | result += "woof! "; 44 | 45 | return result; 46 | } 47 | 48 | 49 | inline std::string Cat::talk(int n_times) 50 | { 51 | std::string result; 52 | 53 | for ( int i = 0 ; i < n_times ; ++i ) 54 | result += "meow! "; 55 | 56 | return result; 57 | } 58 | 59 | 60 | std::string talk(Animal &animal, int n_times) 61 | { 62 | return animal.talk(n_times); 63 | } 64 | 65 | // ----------------------------- 66 | // Python interface - trampoline 67 | // ----------------------------- 68 | 69 | class PyAnimal : public Animal 70 | { 71 | public: 72 | 73 | // inherit the constructors 74 | using Animal::Animal; 75 | 76 | // trampoline (one for each virtual function) 77 | std::string talk(int n_times) override { 78 | PYBIND11_OVERLOAD_PURE( 79 | std::string, /* Return type */ 80 | Animal, /* Parent class */ 81 | talk, /* Name of function in C++ (must match Python name) */ 82 | n_times /* Argument(s) */ 83 | ); 84 | } 85 | }; 86 | 87 | // ---------------- 88 | // Python interface 89 | // ---------------- 90 | 91 | namespace py = pybind11; 92 | 93 | PYBIND11_MODULE(example, m) 94 | { 95 | py::class_(m, "Animal") 96 | .def(py::init<>()) 97 | .def("talk", &Animal::talk); 98 | 99 | py::class_(m, "Dog") 100 | .def(py::init<>()); 101 | 102 | py::class_(m, "Cat") 103 | .def(py::init<>()); 104 | 105 | m.def("talk", &talk, py::arg("animal") ,py::arg("n_times")=1); 106 | } 107 | -------------------------------------------------------------------------------- /11_class-parent-child/pybind11: -------------------------------------------------------------------------------- 1 | ../pybind11 -------------------------------------------------------------------------------- /11_class-parent-child/test.py: -------------------------------------------------------------------------------- 1 | 2 | import example 3 | 4 | print(example.talk(example.Dog(),2)) 5 | print(example.talk(example.Cat(),3)) 6 | -------------------------------------------------------------------------------- /12_crtp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1..3.19) 2 | 3 | project(mymodule) 4 | 5 | find_package(pybind11 CONFIG REQUIRED) 6 | find_package(xtensor REQUIRED) 7 | find_package(xtensor-python REQUIRED) 8 | find_package(Python REQUIRED COMPONENTS NumPy) 9 | 10 | pybind11_add_module(mymodule main.cpp) 11 | target_link_libraries(mymodule PUBLIC pybind11::module xtensor-python Python::NumPy) 12 | 13 | target_compile_definitions(mymodule PRIVATE VERSION_INFO=0.1.0) 14 | -------------------------------------------------------------------------------- /12_crtp/example.py: -------------------------------------------------------------------------------- 1 | import mymodule 2 | import numpy as np 3 | 4 | A = np.array([1, 2, 3]) 5 | 6 | assert np.all(mymodule.Base().myfunc(A) == 2 * A) 7 | assert np.all(mymodule.Derived().myfunc(A) == 3 * A) 8 | -------------------------------------------------------------------------------- /12_crtp/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace py = pybind11; 6 | 7 | template 8 | class BaseImpl 9 | { 10 | public: 11 | 12 | template 13 | T myfunc(const T& t) 14 | { 15 | return static_cast(this)->myfunc_impl(t); 16 | } 17 | 18 | private: 19 | template 20 | T myfunc_impl(const T& t) 21 | { 22 | T ret = t; 23 | for (auto& i : ret) { 24 | i *= 2.0; 25 | } 26 | return ret; 27 | } 28 | }; 29 | 30 | class Base : public BaseImpl 31 | { 32 | public: 33 | Base() = default; 34 | 35 | private: 36 | friend class BaseImpl; 37 | }; 38 | 39 | class Derived : public BaseImpl 40 | { 41 | public: 42 | Derived() = default; 43 | 44 | private: 45 | template 46 | T myfunc_impl(const T& t) 47 | { 48 | T ret = t; 49 | for (auto& i : ret) { 50 | i *= 3.0; 51 | } 52 | return ret; 53 | } 54 | 55 | private: 56 | friend class BaseImpl; 57 | }; 58 | 59 | template 60 | auto registerBaseImpl(M& self) 61 | { 62 | self.def(py::init<>()) 63 | .def("myfunc", &T::template myfunc>); 64 | } 65 | 66 | PYBIND11_MODULE(mymodule, m) 67 | { 68 | m.doc() = "CRTP example"; 69 | 70 | py::class_> BaseBase(m, "BaseBase"); 71 | py::class_> BaseDerived(m, "BaseDerived"); 72 | 73 | registerBaseImpl>(BaseBase); 74 | registerBaseImpl>(BaseDerived); 75 | 76 | py::class_>(m, "Base") 77 | .def(py::init<>()); 78 | 79 | py::class_>(m, "Derived") 80 | .def(py::init<>()); 81 | } 82 | -------------------------------------------------------------------------------- /13_static_cast/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.18) 2 | 3 | project(mymodule) 4 | 5 | set(CMAKE_CXX_STANDARD 14) 6 | 7 | find_package(pybind11 REQUIRED CONFIG) 8 | pybind11_add_module(mymodule code_and_wrapper.cpp) 9 | -------------------------------------------------------------------------------- /13_static_cast/code_and_wrapper.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // ------------------ 5 | // regular C++ header 6 | // ------------------ 7 | 8 | namespace mymodule { 9 | 10 | class Foo 11 | { 12 | public: 13 | 14 | Foo() = default; 15 | 16 | template 17 | T bar(const T& a) const 18 | { 19 | T ret = a; 20 | for (auto& i : ret) { 21 | i *= 2.0; 22 | } 23 | return ret; 24 | } 25 | 26 | template 27 | T bar(const T& a, double f) const 28 | { 29 | T ret = a; 30 | for (auto& i : ret) { 31 | i *= f; 32 | } 33 | return ret; 34 | } 35 | 36 | }; 37 | 38 | } // namespace mymodule 39 | 40 | // ---------------- 41 | // Python interface 42 | // ---------------- 43 | 44 | namespace py = pybind11; 45 | 46 | PYBIND11_MODULE(mymodule, m) 47 | { 48 | py::class_(m, "Foo") 49 | .def(py::init<>()) 50 | 51 | .def("bar", 52 | static_cast (mymodule::Foo::*)(const std::vector&) const>(&mymodule::Foo::bar), 53 | py::arg("a")) 54 | 55 | .def("bar", 56 | static_cast (mymodule::Foo::*)(const std::vector&, double) const>(&mymodule::Foo::bar), 57 | py::arg("a"), 58 | py::arg("f")); 59 | } 60 | -------------------------------------------------------------------------------- /13_static_cast/test.py: -------------------------------------------------------------------------------- 1 | import mymodule 2 | 3 | print(mymodule.Foo().bar([1, 2, 3])) 4 | print(mymodule.Foo().bar([1, 2, 3], 3)) 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Tom de Geus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Contents 2 | 3 | 4 | 5 | - [Introduction](#introduction) 6 | - [Cloning this repository](#cloning-this-repository) 7 | - [Dependencies](#dependencies) 8 | - [Compiling strategies](#compiling-strategies) 9 | - [DIY](#diy) 10 | - [CMake](#cmake) 11 | - [Basic usage](#basic-usage) 12 | - [Using Eigen](#using-eigen) 13 | - [Using the C++14 standard](#using-the-c14-standard) 14 | - [setup.py](#setuppy) 15 | - [Examples](#examples) 16 | - [01_py-list_cpp-vector](#01_py-list_cpp-vector) 17 | - [02_py-nested-list_cpp-nested-vector](#02_py-nested-list_cpp-nested-vector) 18 | - [03_numpy-1D_cpp-vector](#03_numpy-1d_cpp-vector) 19 | - [04_numpy-2D_cpp-vector](#04_numpy-2d_cpp-vector) 20 | - [05_numpy-2D_cpp-eigen](#05_numpy-2d_cpp-eigen) 21 | - [06_class-numpy-eigen](#06_class-numpy-eigen) 22 | - [07_cpp-overload-scalar](#07_cpp-overload-scalar) 23 | - [08_cpp-overload-eigen](#08_cpp-overload-eigen) 24 | - [09_numpy_cpp-custom-matrix](#09_numpy_cpp-custom-matrix) 25 | - [10_enum](#10_enum) 26 | - [11_class-parent-child](#11_class-parent-child) 27 | - [12_crtp](#12_crtp) 28 | - [13_static_cast](#13_static_cast) 29 | 30 | 31 | 32 | 33 | # Introduction 34 | 35 | The power of [pybind11](https://github.com/pybind/pybind11) is captured by the following citation from pybind11's readme: 36 | 37 | > 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 ... 38 | > 39 | > 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. 40 | 41 | This repository contains several examples for the usage of pybind11. Even though the [online documentation](http://pybind11.readthedocs.io) 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](https://github.com/tdegeus/pybind11_examples), or [contact me](mailto:tom_AT_geus.me). 42 | 43 | To give credit where credit is due: 44 | 45 | * The creators of pybind11 have done a great job! It is really easy to use, and very lightweight. Also the [documentation](http://pybind11.readthedocs.io) is already quite complete. 46 | 47 | * The examples made available by [Cliburn Chan and Janice McCarthy, at Duke University](http://people.duke.edu/~ccc14/sta-663-2016/18G_C++_Python_pybind11.html) have been of enormous help. Please also read their [documentation](http://people.duke.edu/~ccc14/sta-663-2016/18G_C++_Python_pybind11.html). 48 | 49 | Note that there are also test cases that double as examples in the [pybind11 repository](https://github.com/pybind/pybind11), but these are not very insightful when you are new to pybind11. 50 | 51 | Finally, pybind11 is actively used. So one can look in actively maintained libraries for specific solutions. For example: 52 | 53 | * [cppmat](http://cppmat.geus.me) is a library that provided multidimensional arrays in C++, much like the [Eigen](http://eigen.tuxfamily.org) 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. 54 | 55 | # Cloning this repository 56 | 57 | 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: 58 | 59 | * The simplest option is: 60 | 61 | ```bash 62 | git clone --recursive https://github.com/tdegeus/pybind11_examples.git 63 | ``` 64 | 65 | 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: 66 | 67 | ```bash 68 | git submodule update --remote 69 | ``` 70 | 71 | * One could also directly download the submodule from the source: 72 | 73 | ```bash 74 | git clone https://github.com/tdegeus/pybind11_examples.git 75 | cd pybind11_examples 76 | git submodule init 77 | git submodule update 78 | ``` 79 | 80 | # Dependencies 81 | 82 | The [Eigen](http://eigen.tuxfamily.org) 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. 83 | 84 | 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. 85 | 86 | In general one can download and install Eigen by: 87 | 88 | ```bash 89 | mkdir /path/to/temp/build 90 | cmake /path/to/eigen/download 91 | make install 92 | ``` 93 | 94 | > For macOS one could simply use 95 | > 96 | > ```bash 97 | > brew install eigen 98 | > ``` 99 | 100 | Thereafter compile with 101 | 102 | ```bash 103 | clang++ -I/path/to/eigen/instalation ... 104 | ``` 105 | 106 | Note that, when properly configured (which is normally the case), pkg-config can be used to keep track of the paths: 107 | 108 | ```bash 109 | clang++ `pkg-config --cflags eigen3` ... 110 | ``` 111 | 112 | Or one can use CMake (see below). 113 | 114 | # Compiling strategies 115 | 116 | ## DIY 117 | 118 | 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 119 | 120 | ```bash 121 | c++ -O3 -shared -std=gnu++11 -I ./pybind11/include `python3-config --cflags --ldflags --libs` example.cpp -o example.so -fPIC 122 | ``` 123 | 124 | ## CMake 125 | 126 | ### Basic usage 127 | 128 | 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: 129 | 130 | ```cmake 131 | cmake_minimum_required(VERSION 2.8.12) 132 | project(example) 133 | 134 | add_subdirectory(pybind11) 135 | pybind11_add_module(example example.cpp) 136 | ``` 137 | 138 | (whereby this example module consists of a single source file `example.cpp`). To compile it, use: 139 | 140 | ```bash 141 | cd /path/to/build 142 | cmake /path/to/example/src 143 | make 144 | ``` 145 | 146 | ### Using Eigen 147 | 148 | When Eigen is 'installed' it can be easily included by adding the following in `CMakeLists.txt`: 149 | 150 | ```cmake 151 | find_package( PkgConfig ) 152 | pkg_check_modules( EIGEN3 REQUIRED eigen3 ) 153 | include_directories( ${EIGEN3_INCLUDE_DIRS} ) 154 | ``` 155 | 156 | ### Using the C++14 standard 157 | 158 | The C++14 standard can be used by including the following in `CMakeLists.txt`: 159 | 160 | ```cmake 161 | set(CMAKE_CXX_STANDARD 14) 162 | ``` 163 | 164 | ## setup.py 165 | 166 | A file `setup.py` can be added to your library. You can then compile and install using 167 | 168 | ```bash 169 | python3 setup.py build 170 | python3 setup.py install 171 | ``` 172 | 173 | Although writing the `setup.py` is not hard it is not covered here. One can use some tools included in [cppmat](http://cppmat.geus.me), that can be installed with pip (e.g. `pip3 install cppmat`). Furthermore on can look at the `setup.py` of for example [GooseTensor](https://github.com/tdegeus/GooseTensor) or several other of [my repositories](https://github.com/tdegeus). 174 | 175 | # Examples 176 | 177 | ## [01_py-list_cpp-vector](01_py-list_cpp-vector) 178 | 179 | 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`](01_py-list_cpp-vector/example.cpp)). From Python this function is contained in a simple module `example` (see [`test.py`](01_py-list_cpp-vector/test.py)). 180 | 181 | 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. 182 | 183 | To compile, either employ CMake, whereby the compilation instructions are read from [`CMakeLists.txt`](01_py-list_cpp-vector/CMakeLists.txt) by subsequently: 184 | 185 | ```bash 186 | cmake . 187 | make 188 | ``` 189 | 190 | Or, compile directly using: 191 | 192 | ```bash 193 | c++ -O3 -shared -std=gnu++11 -I ./pybind11/include `python3-config --cflags --ldflags --libs` example.cpp -o example.so -fPIC 194 | ``` 195 | 196 | Run the example by: 197 | 198 | ```bash 199 | python3 test.py 200 | ``` 201 | 202 | > To run with Python 2, simply replace the two occurrences of "python3" above with "python". To modify the CMake instructions find more [online](http://pybind11.readthedocs.io/en/master/compiling.html?highlight=cmake). 203 | 204 | ## [02_py-nested-list_cpp-nested-vector](02_py-nested-list_cpp-nested-vector) 205 | 206 | Same as the [previous example](#01py-listcpp-vector), but with a nested list. 207 | 208 | ## [03_numpy-1D_cpp-vector](03_numpy-1D_cpp-vector) 209 | 210 | A function `modify` that converts the entries from a one-dimensional array to integers, and then multiplies these entries by 10. 211 | 212 | 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. 213 | 214 | ## [04_numpy-2D_cpp-vector](04_numpy-2D_cpp-vector) 215 | 216 | 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. 217 | 218 | ## [05_numpy-2D_cpp-eigen](05_numpy-2D_cpp-eigen) 219 | 220 | Two functions `det` and `inv` that use the Eigen library. 221 | 222 | The purpose of this example is to show how trivial the interaction between C++/Eigen and Python/NumPy is. 223 | 224 | To compile using CMake and to run, follow the instructions [above](#01py-listcpp-vector) (whereby the Eigen headers are included in [`CMakeLists.txt`](05_numpy-2D_cpp-eigen/CMakeLists.txt). To compile directly, additionally the Eigen headers have to be included: 225 | 226 | ```bash 227 | c++ -O3 -shared -std=gnu++11 -I /path/to/eigen -I ./pybind11/include `python3-config --cflags --ldflags --libs` example.cpp -o example.so -fPIC 228 | ``` 229 | 230 | For example on macOS with homebrew: 231 | 232 | ```bash 233 | 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 234 | ``` 235 | 236 | ## [06_class-numpy-eigen](06_class-numpy-eigen) 237 | 238 | A custom `CustomVectorXd` class with one function `mul`. This class uses the Eigen library. It also includes a default argument. 239 | 240 | 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). 241 | 242 | ## [07_cpp-overload-scalar](07_cpp-overload-scalar) 243 | 244 | 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`). 245 | 246 | To compile, make sure that the C++14 standard is used, for example by including `-std=c++14` as compiler argument. 247 | 248 | ## [08_cpp-overload-eigen](08_cpp-overload-eigen) 249 | 250 | Similar to the [previous example](#08cpp-overload-eigen), but with Eigen arguments (i.e. NumPy arguments from the Python side). 251 | 252 | To compile, make sure that the C++14 standard is used. 253 | 254 | ## [09_numpy_cpp-custom-matrix](09_numpy_cpp-custom-matrix) 255 | 256 | 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. 257 | 258 | See also [this](http://stackoverflow.com/questions/42645228/cast-numpy-array-to-from-custom-c-matrix-class-using-pybind11) discussion of Stack Overflow. 259 | 260 | ## [10_enum](10_enum) 261 | 262 | 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++. 263 | 264 | ## [11_class-parent-child](11_class-parent-child) 265 | 266 | 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. 267 | 268 | This particular case requires more involved interface, as is described in [the documentation](http://pybind11.readthedocs.io/en/stable/advanced/classes.html). 269 | 270 | ## [12_crtp](12_crtp) 271 | 272 | This example features a simple CRTP with as 'base' and and a 'derived' class, 273 | and its registration to the *pybind11* API. 274 | 275 | ## [13_static_cast](13_static_cast) 276 | 277 | Sometimes `py::overload_cast` is not able to resolve your function, 278 | for example when the return type cannot be inferred. 279 | In that case you can be explicit by `static_cast`ing a pointer to your function 280 | More information can be found in [the documentation](https://pybind11.readthedocs.io/en/stable/classes.html?highlight=static_cast#overloaded-methods). 281 | 282 | --------------------------------------------------------------------------------