Recipe17.3.Exposing a C Library to Python


Recipe 17.3. Exposing a C++ Library to Python

Credit: Ralf W. Grosse-Kunstleve, David Abrahams

Problem

You want to use a C++ library in Python. For example, you might have a fast rational-numbers library, coded in C++, that you wish to wrap for use from Python.

Solution

Boost, http://www.boost.org, is a large free package with more than 50 fast and solid C++ libraries. Among those libraries, we find both Boost.Rational, a rational number library, and Boost.Python, which makes it easy to turn any other C++ library into a Python extension. So, we simply use the latter to wrap the former:

#include <boost/python.hpp> #include <boost/rational.hpp> /* two specific conversion functions: rational to float and to str */ static double as_float(boost::rational<int> const& self) {   return double(self.numerator( )) / self.denominator( ); } static boost::python::object as_str(boost::rational<int> const& self) {   using boost::python::str;   if (self.denominator( ) == 1) return str(self.numerator( ));   return str(self.numerator( )) + "/" + str(self.denominator( )); } /* the 'rational' Python extension module, with just one class in it: */ BOOST_PYTHON_MODULE(rational) {   boost::python::class_<boost::rational<int> >("int")     .def(boost::python::init<int, optional<int> >( ))     .def("numerator", &boost::rational<int>::numerator)     .def("denominator", &boost::rational<int>::denominator)     .def("_ _float_ _", as_float)     .def("_ _str_ _", as_str)     .def(-self)     .def(self + self)     .def(self - self)     .def(self * self)     .def(self / self)     .def(self + int( ))     .def(self - int( ))     .def(self * int( ))     .def(self / int( ))     .def(int( ) + self)     .def(int( ) - self)     .def(int( ) * self)     .def(int( ) / self)   ; }

Discussion

Once you have built and installed the rational extension shown in this recipe's Solution, you can use it to perform simple, natural tasks, such as:

>>> import rational >>> x = rational.int(1, 3) >>> y = rational.int(-2, 4) >>> print "x =", x x = 1/3 >>> print "y =", y y = -1/2 >>> print "x+y =", x+y x+y = -1/6 >>> print "x*2 =", x * 2 x*2 = 2/3 >>> print "3/y =", 3 / y 3/y = -6

Compiling and linking Boost.Python extensions is supported by the Boost.Build tool; we do not cover that topic here. Extensive documentation is available online at the Boost site. Such tools as make and SCons are also popular for software compilation and linking tasks, including tasks that involve Boost.

The Solution's code shows off a few of Boost.Python's powerful features. Consider the snippet:

BOOST_PYTHON_MODULE(rational) {   class_<boost::rational<int> >("int")     ...

The BOOST_PYTHON_MODULE macro takes a module name as a parameter, and a module body immediately afterwards within braces, and does all that's needed to make a module we can import from Python.

The class_ template, instantiated with the boost::rational type as a parameter and "called" with the string argument "int", does all we need to have as part of our module a Python-usable class, named rational.int, each of whose instances wraps an instance of the boost::rational class. The type boost::rational is itself a template, and we instantiate that template with int as a parameter, to use int as the type of each rational number's numerator and denominator.

If we stopped here, wrapping a C++ class in the class_ template, and exposing the wrapper without adding any more to it, we'd have a rather empty type available on the Python side. It would have no constructor (save for the default argument-less one), no methods, and no attributes. To remedy this, the Solution code continues with several .def(...) calls, which are chained: each call enriches the object, and also returns it, so you can just string such calls one after the other. The methods we add with all those def calls include a constructor (which uses the init template), then a couple of ordinary methods that delegate to the methods of the same names in the wrapped class (accessors to the numerator and denominator parts of a rational number), and then a couple of type-conversion special methods for which we've previously coded corresponding functions (just before the BOOST_PYTHON_MODULE macro). Note, in particular, that the implementation of the as_str function is so concise because it makes full use of Boost.Python's object interfaceit's almost like writing Python in C++.

The baker's dozen of .def(...) calls that begins with:

    .def(-self)

and proceeds all the way to:

    .def(int( ) / self)

exposes all the arithmetic special methods for our new rational.int classunary minus (_ _neg_ _), and the four operations, each in three versionsbetween two instances of our class, and between such instances and ints on either side (_ _add_ _, _ _radd_ _, etc.). The magic is performed using expression templates, a technique originally developed for optimizing high-performance matrix algebra expressions. Boost.Python's use of expression templates has a different purpose, but it certainly comes in handy anyway!

A comprehensive rational number extension would require more functionalitycomparison operators, _ _repr_ _, _ _hash_ _, support for pickling, and so on. A more complete implementation, one that is actively used in applications, can be found at http://cvs.sourceforge.net/viewcvs.py/cctbx/boost_adaptbx/, in the file rational_ext.cpp.

See Also

Boost's site is http://www.boost.org; the rational number library Boost.Rational, is at http://www.boost.org/libs/rational; Boost.Python is at http://www.boost.org/libs/python.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net