2.4. Wrap C++ with Boost.Python¶

Boost is a high-quality, widely-used, open-source C++ library. Boost.Python is one component project that provides a comprehensive wrapping capabilities between C++ and Python. By using Boost.Python, one can easily create a Python extension module with C++.

2.4.1. Create a Python Extension¶

The basic and the most important feature of Boost.Python is to help writing Python extension modules by using C++.

This is our first Python extension module by Boost.Python; call it zoo.cpp:

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 /* * This inclusion should be put at the beginning. It will include . */ #include #include /* * This is the C++ function we write and want to expose to Python. */ const std::string hello() { return std::string("hello, zoo"); } /* * This is a macro Boost.Python provides to signify a Python extension module. */ BOOST_PYTHON_MODULE(zoo) { // An established convention for using boost.python. using namespace boost::python; // Expose the function hello(). def("hello", hello); } // vim: set ai et nu sw=4 ts=4 tw=79: 

It simply return a string from C++ to Python. Boost.Python will do all the conversion and interfacing for us:

 1 2 3 4 5 6 7 import zoo # In zoo.cpp we expose hello() function, and it now exists in the zoo module. assert 'hello' in dir(zoo) # zoo.hello is a callable. assert callable(zoo.hello) # Call the C++ hello() function from Python. print zoo.hello() 

Running the above script (call it visit_zoo.py) will get:

hello, zoo


The following makefile will help us build the module (and run it):

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 CC = g++ PYLIBPATH = $(shell python-config --exec-prefix)/lib LIB = -L$(PYLIBPATH) $(shell python-config --libs) -lboost_python OPTS =$(shell python-config --include) -O2 default: zoo.so @python ./visit_zoo.py zoo.so: zoo.o $(CC)$(LIB) -Wl,-rpath,$(PYLIBPATH) -shared$< -o $@ zoo.o: zoo.cpp Makefile$(CC) $(OPTS) -c$< -o \$@ clean: rm -rf *.so *.o .PHONY: default clean 

2.4.2. Wrap a Class¶

Expose a class Animal from C++ to Python:

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 /* * This inclusion should be put at the beginning. It will include . */ #include #include #include #include #include #include /* * This is the C++ function we write and want to expose to Python. */ const std::string hello() { return std::string("hello, zoo"); } /* * Create a C++ class to represent animals in the zoo. */ class Animal { public: // Constructor. Note no default constructor is defined. Animal(std::string const & in_name): m_name(in_name) {} // Copy constructor. Animal(Animal const & in_other): m_name(in_other.m_name) {} // Copy assignment. Animal & operator=(Animal const & in_other) { this->m_name = in_other.m_name; return *this; } // Utility method to get the address of the instance. uintptr_t get_address() const { return reinterpret_cast(this); } // Getter of the name property. std::string get_name() const { return this->m_name; } // Setter of the name property. void set_name(std::string const & in_name) { this->m_name = in_name; } private: // The only property: the name of the animal. std::string m_name; }; /* * This is a macro Boost.Python provides to signify a Python extension module. */ BOOST_PYTHON_MODULE(zoo) { // An established convention for using boost.python. using namespace boost::python; // Expose the function hello(). def("hello", hello); // Expose the class Animal. class_("Animal", init()) .def("get_address", &Animal::get_address) .add_property("name", &Animal::get_name, &Animal::set_name) ; } // vim: set ai et nu sw=4 ts=4 tw=79: 

The script changes to:

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import zoo # In zoo.cpp we expose hello() function, and it now exists in the zoo module. assert 'hello' in dir(zoo) # zoo.hello is a callable. assert callable(zoo.hello) # Call the C++ hello() function from Python. print zoo.hello() # Create an animal. animal = zoo.Animal("dog") # The Python object. print animal # Use the exposed method to show the address of the C++ object. print "The C++ object is at 0x%016x" % animal.get_address() # Use the exposed property accessor. print "I see a \"%s\"" % animal.name animal.name = "cat" print "I see a \"%s\"" % animal.name 

The output is:

hello, zoo
<zoo.Animal object at 0x102437890>
The C++ object is at 0x00007fb0c860ac20
I see a "dog"
I see a "cat"