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 <Python.h>.
*/
#include <boost/python.hpp>
#include <string>
/*
* 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 <Python.h>.
*/
#include <boost/python.hpp>
#include <cstdint>
#include <string>
#include <vector>
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
/*
* 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<uintptr_t>(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>("Animal",
init<std::string const &>())
.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"