Boost.Python
domesticating the snake
Sławomir Zborowski @ 
Agenda
C++ vs Python
Why Boost.Python?
Extending
Interchanging objects
Embedding
C++ vs Python
C++ Python
statically typed dynamically typed
strongly typed strongly typed
federation of languages object oriented
efficient flexible
compiled interpreted
manual memory
management
garbage collected
hard? easy
Why Boost.Python?
→ raw, C, reference counting ☹
→ custom tools, custom language ☹
→ custom tools, custom language ☹
→ easy, only C++ ☺
Python API
SWIG
SIP
Boost.Python
Extending vs embedding
Building
1cmake_minimum_required(VERSION2.8.9)
2project(CppUsersWroclaw)
3
4find_package(PythonLibsREQUIRED)
5find_package(BoostREQUIREDpython)
6
7include_directories(${PYTHON_INCLUDE_DIRS}
8 ${Boost_INCLUDE_DIRS})
9
10set(CMAKE_CXX_FLAGS"-Wall-Wextra-O3-std=c++14")
11
12add_library(${PROJECT_NAME}SHAREDmain.cpp)
13
14target_link_libraries(${PROJECT_NAME}
15 ${Boost_PYTHON_LIBRARY})
16
17set_target_properties(${PROJECT_NAME}PROPERTIESPREFIX"")
Exposing functions
1#include<boost/python.hpp>
2#include<string>
3
4namespacebp=boost::python;
5
6voidhelloWroclaw(std::stringconst&name){
7 std::cout<<"Hello,"<<name<<"!"<<std::endl;
8}
9
10BOOST_PYTHON_MODULE(CppUsersWroclaw)
11{
12 bp::def("helloWroclaw",helloWroclaw);
13}
Exposing functions
1frombuildimportCppUsersWroclaw
2
3CppUsersWroclaw.helloWroclaw('WroclawC++users')
szborowsatprobookin~/D/p/b/code
↪pythontest.py
Hello,WroclawC++users!
Exposing functions
default arguments require thin wrappers
overloaded functions are a bit tricky
Exposing classes
1structResult{
2 longvalue=0;
3 staticResultcreate(longconstvalue){
4 returnResult{value};
5 }
6private:
7 explicitResult(longconstvalue):value(value){}
8};
9
10classCalculator{
11 std::unordered_map<long,Result>cache_;
12public:
13 Resultcalculate(longconstcoeff){
14 if(cache_.find(coeff)==cache_.end()){
15 Resultr=Result::create(42);//...
16 cache_.insert(std::make_pair(coeff,r));
17 }
18 returncache_.find(coeff)->second;
19 }
20};
Exposing classes
1bp::class_<Result>("Result",bp::no_init)
2 .def_readonly("value",&Result::value);
3
4bp::class_<Calculator>("Calculator")
5 .def("calculate",&Calculator::calculate);
1frombuildimportCppUsersWroclaw
2
3classWrappedCalculator(CppUsersWroclaw.Calculator):
4 defcalculate(self,coeff):
5 print'Callingoriginalcalculate...'
6 returnCppUsersWroclaw.Calculator.calculate(self,coeff)
7
8result=WrappedCalculator().calculate(69L)
9printresult.value
szborowsatprobookin~/D/p/b/code
↪pythontest2.py
Callingoriginalcalculate...
42
Exposing classes
explicit exposure
member functions: d e f
member variables: d e f _ r e a d o n l y , d e f _ r e a d w r i t e
c-tors: n o _ i n i t , i n i t (multiple allowed)
properties: a d d _ p r o p e r t y (ro/rw)
Inheritance
35structBase{
36 virtual~Base()=default;
37 virtualvoidvirtualMethod()const{
38 std::cout<<"Base::virtualMethodn";
39 }
40
41 voidnormalMethod()const{
42 std::cout<<"Base::normalMethodn";
43 }
44};
45
46structDerived:Base{
47 virtualvoidvirtualMethod()constoverride{
48 std::cout<<"Derived::virtualMethodn";
49 }
50
51 voidnormalMethod()const{
52 std::cout<<"Derived::normalMethodn";
53 }
54};
55
56Base*createDerived(){returnnewDerived;}
Inheritance
65 bp::class_<Base>("Base")
66 .def("virtualMethod",&Base::virtualMethod)
67 .def("normalMethod",&Base::normalMethod)
68 ;
69
70 bp::class_<Derived,bp::bases<Base>>("Derived");
71
72 bp::def("createDerived",createDerived,
73 bp::return_value_policy<bp::manage_new_object>());
74}
1frombuild.CppUsersWroclawimport*
2
3b=createDerived()
4
5b.virtualMethod()
6b.normalMethod()
szborowsatprobookin~/D/p/b/code
↪pythontest3.py
Derived::virtualMethod
Base::normalMethod
Special methods
7template<typenameT>
8structVector{
9 Vector(Tx,Ty,Tz)
10 :x(x),y(y),z(z){}
11
12 Tx={},y={},z={};
13
14 Vector&operator+=(Vectorconst&other){
15 std::cout<<"Vector::operator+=n";
16 x+=other.x;
17 y+=other.y;
18 z+=other.z;
19 return*this;
20 }
21
22 Vectoroperator+(Vectorconst&other){
23 std::cout<<"Vector::operator+n";
24 returnVector{
25 x+other.x,
26 y+other.y,
27 z+other.z};
28 }
29
30};
Special methods
29 bp::class_<Vector<float>>("Vector",bp::init<float,float,float>())
30 .def_readonly("x",&Vector<float>::x)
31 .def_readonly("y",&Vector<float>::y)
32 .def_readonly("z",&Vector<float>::z)
33 .def(bp::self+bp::other<Vector<float>>{})
34 .def(bp::self+=bp::other<Vector<float>>{})
35 ;
1frombuild.CppUsersWroclawimport*
2
3v1=Vector(1.0,2.0,3.0)
4v2=Vector(2.0,3.0,4.0)
5
6v3=v1+v2
7v2+=v1
szborowsatprobookin~/D/p/b/code
↪pythontest4.py
Vector::operator+
Vector::operator+=
Special methods
C++ Python
o p e r a t o r + _ _ a d d _ _
_ _ r a d d _ _
o p e r a t o r - _ _ s u b _ _
o p e r a t o r + = _ _ i a d d _ _
o p e r a t o r - = _ _ i s u b _ _
o p e r a t o r < _ _ l t _ _
o p e r a t o r < = _ _ l e _ _
b p : : s t r _ _ s t r _ _
b p : : a b s _ _ a b s _ _
b p : : p o w _ _ p o w _ _
source: [python operators]
Exposing constants & enums
60constexprautoFAMOUS_CONSTANT=42;
61
62enumclassRole{
63 JuniorDev,
64 Dev,
65 SeniorDev,
66 ForgottenEmployee
67};
92 bp::scope().attr("FAMOUS_CONSTANT")=FAMOUS_CONSTANT;
93
94 bp::enum_<Role>("Role")
95 .value("JuniorDev",Role::JuniorDev)
96 .value("Dev",Role::Dev)
97 .value("SeniorDev",Role::SeniorDev)
98 .value("ForgottenEmployee",Role::ForgottenEmployee)
99 ;
Exposing constants & enums
1frombuild.CppUsersWroclawimport*
2
3printFAMOUS_CONSTANT
4
5role=Role.ForgottenEmployee
6printrole,'=',int(role)
szborowsatprobookin~/D/p/b/code
↪pythontest5.py
42
ForgottenEmployee=3
Boost.Python objects
Interchanging objects
1frombuild.CppUsersWroclawimport*
2
3x=[
4 Vector(float(i),float(i),float(i))
5 foriinrange(10)
6]
7
8x=list(reversed(x))
9
10forvinsortVectors(x):
11 printv.x,v.y,v.z
Interchanging objects
73bp::objectsortVectors(bp::objectvectors){
74 bp::listlist=bp::extract<bp::list>(vectors);
75
76 vector<Vector<float>>result;
77 for(size_ti=0;i<bp::len(list);++i){
78 result.push_back(bp::extract<Vector<float>>(list[i]));
79 }
80
81 sort(begin(result),end(result),
82 [](autoconst&lhs,autoconst&rhs)
83 {returnlhs.x<rhs.x;});
84
85 bp::listsorted;
86 for(autoconst&e:result){
87 sorted.append(e);
88 }
89
90 returnsorted;
91}
szborowsatprobookin~/D/p/b/code
↪pythontest5.py
0.01.02.03.04.05.06.07.08.09.0
Interchanging objects
Python C++
i n t ,
f l o a t ,
b o o l , …
i n t , f l o a t ,
b o o l , …
o b j e c t o b j e c t
l i s t b p : : l i s t
d i c t b p : : d i c t
s t r b p : : s t r
l o n g b p : : l o n g _
* b p : : e x t r a c t < >
collections → indexing suites
custom types → converters
⇒
Cumbersomeness
call policies
non-trivial members require proxies
iterable types
lazy evaluation
compilation time
move-semantics
exceptions
multithreading
⇒
⇒
⇒
⇒
Embedding Python in C++
9intmain(){
10 Py_Initialize();
11 automainModule=bp::import("__main__");
12 automainNamespace=mainModule.attr("__dict__");
13
14 mainNamespace["messageFromCpp"]="I'mstringfromC++!"s;
15
16 try{
17 bp::exec(R"^(
18printmessageFromCpp
19response='HelloC++!Ialsosupportstrings;)'
20 )^",mainNamespace);
21 stringresponse=bp::extract<string>(mainNamespace["response"]);
22 cout<<response<<endl;
23 }
24 catch(bp::error_already_set){
25 PyErr_Print();
26 }
27}
szborowsatprobookin~/D/p/b/code
↪./embedded_python
I'mstringfromC++!
HelloC++!Ialsosupportstrings;)
Q & A

Boost.Python - domesticating the snake