Python Programming/Extending with C++
There are different ways to extend Python with C and C++ code:
- In plain C, using Python.h
- Using Swig
- Using Boost.Python, optionally with Py++ preprocessing
- Using pybind11
- Using Cython.
This page describes Boost.Python. Before the emergence of Cython, it was the most comfortable way of writing C++ extension modules.
Boost.Python comes bundled with the Boost C++ Libraries. To install it on an Ubuntu system, you might need to run the following commands
$ sudo apt-get install libboost-python-dev $ sudo apt-get install python-dev
A Hello World Example
[edit | edit source ]The C++ source code (hellomodule.cpp)
[edit | edit source ]#include<iostream> usingnamespacestd; voidsay_hello(constchar*name){ cout<<"Hello "<<name<<"!\n"; } #include<boost/python/module.hpp> #include<boost/python/def.hpp> usingnamespaceboost::python; BOOST_PYTHON_MODULE(hello) { def("say_hello",say_hello); }
setup.py
[edit | edit source ]#!/usr/bin/env python fromdistutils.coreimport setup fromdistutils.extensionimport Extension setup(name="PackageName", ext_modules=[ Extension("hello", ["hellomodule.cpp"], libraries = ["boost_python"]) ])
Now we can build our module with
python setup.py build
The module `hello.so` will end up in e.g `build/lib.linux-i686-2.4`.
Using the extension module
[edit | edit source ]Change to the subdirectory where the file `hello.so` resides. In an interactive python session you can use the module as follows.
>>> import hello >>> hello.say_hello("World") Hello World!
An example with CGAL
[edit | edit source ]Some, but not all, functions of the CGAL library already have Python bindings. Here an example is provided for a case without such a binding and how it might be implemented. The example is taken from the CGAL Documentation.
// test.cpp usingnamespacestd; /* PYTHON */ #include<boost/python.hpp> #include<boost/python/module.hpp> #include<boost/python/def.hpp> namespacepython=boost::python; /* CGAL */ #include<CGAL/Cartesian.h> #include<CGAL/Range_segment_tree_traits.h> #include<CGAL/Range_tree_k.h> typedefCGAL::Cartesian<double>K; typedefCGAL::Range_tree_map_traits_2<K,char>Traits; typedefCGAL::Range_tree_2<Traits>Range_tree_2_type; typedefTraits::KeyKey; typedefTraits::IntervalInterval; Range_tree_2_type*Range_tree_2=newRange_tree_2_type; voidcreate_tree(){ typedefTraits::KeyKey; typedefTraits::IntervalInterval; std::vector<Key>InputList,OutputList; InputList.push_back(Key(K::Point_2(8,5.1),'a')); InputList.push_back(Key(K::Point_2(1.0,1.1),'b')); InputList.push_back(Key(K::Point_2(3,2.1),'c')); Range_tree_2->make_tree(InputList.begin(),InputList.end()); Intervalwin(Interval(K::Point_2(1,2.1),K::Point_2(8.1,8.2))); std::cout<<"\n Window Query:\n"; Range_tree_2->window_query(win,std::back_inserter(OutputList)); std::vector<Key>::iteratorcurrent=OutputList.begin(); while(current!=OutputList.end()){ std::cout<<" "<<(*current).first.x()<<","<<(*current).first.y() <<":"<<(*current).second<<std::endl; current++; } std::cout<<"\n Done\n"; } voidinitcreate_tree(){;} usingnamespaceboost::python; BOOST_PYTHON_MODULE(test) { def("create_tree",create_tree,""); }
// setup.py #!/usr/bin/env python fromdistutils.coreimportsetup fromdistutils.extensionimportExtension setup(name="PackageName", ext_modules=[ Extension("test",["test.cpp"], libraries=["boost_python"]) ])
We then compile and run the module as follows:
$ python setup.py build $ cd build/lib* $ python >>> import test >>> test.create_tree() Window Query: 3,2.1:c 8,5.1:a Done >>>
Handling Python objects and errors
[edit | edit source ]One can also handle more complex data, e.g. Python objects like lists. The attributes are accessed with the extract function executed on the objects "attr" function output. We can also throw errors by telling the library that an error has occurred and returning. In the following case, we have written a C++ function called "afunction" which we want to call. The function takes an integer N and a vector of length N as input, we have to convert the python list to a vector of strings before calling the function.
#include<vector> usingnamespacestd; void_afunction_wrapper(intN,boost::python::listmapping){ intmapping_length=boost::python::extract<int>(mapping.attr("__len__")()); //Do Error checking, the mapping needs to be at least as long as N if(mapping_length<N){ PyErr_SetString(PyExc_ValueError, "The string mapping must be at least of length N"); boost::python::throw_error_already_set(); return; } vector<string>mystrings(mapping_length); for(inti=0;i<mapping_length;i++){ mystrings[i]=boost::python::extract<charconst*>(mapping[i]); } //now call our C++ function _afunction(N,mystrings); } usingnamespaceboost::python; BOOST_PYTHON_MODULE(c_afunction) { def("afunction",_afunction_wrapper); }
External links
[edit | edit source ]- Boost.Python, boost.org
- pybind11, pypi.org