32
Boost.Python domesticating the snake Sławomir Zborowski @

Boost.Python - domesticating the snake

Embed Size (px)

Citation preview

Boost.Pythondomesticating the snake

Sławomir Zborowski @ 

Agenda

C++ vs PythonWhy Boost.Python?ExtendingInterchanging objectsEmbedding

C++ vs Python

C++ Pythonstatically typed dynamically typedstrongly typed strongly typedfederation of languages object orientedefficient flexiblecompiled interpretedmanual memorymanagement

garbage collected

hard? easy

Why Boost.Python?

→ raw, C, reference counting ☹ → custom tools, custom language ☹

→ custom tools, custom language ☹ → easy, only C++ ☺

Python APISWIGSIPBoost.Python

Extending vs embedding

Building 1 cmake_minimum_required(VERSION 2.8.9) 2 project(CppUsersWroclaw) 3 4 find_package(PythonLibs REQUIRED) 5 find_package(Boost REQUIRED python) 6 7 include_directories(${PYTHON_INCLUDE_DIRS} 8 ${Boost_INCLUDE_DIRS}) 9 10 set(CMAKE_CXX_FLAGS "-Wall -Wextra -O3 -std=c++14")11 12 add_library(${PROJECT_NAME} SHARED main.cpp)13 14 target_link_libraries(${PROJECT_NAME}15 ${Boost_PYTHON_LIBRARY})16 17 set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")

Exposing functions 1 #include <boost/python.hpp> 2 #include <string> 3 4 namespace bp = boost::python; 5 6 void helloWroclaw(std::string const& name) { 7 std::cout << "Hello, " << name << "!" << std::endl; 8 } 9 10 BOOST_PYTHON_MODULE(CppUsersWroclaw)11 {12 bp::def("helloWroclaw", helloWroclaw);13 }

Exposing functions1 from build import CppUsersWroclaw2 3 CppUsersWroclaw.helloWroclaw('Wroclaw C++ users')

szborows at probook in ~/D/p/b/code↪ python test.pyHello, Wroclaw C++ users!

Exposing functions

default arguments require thin wrappersoverloaded functions are a bit tricky

Exposing classes 1 struct Result { 2 long value = 0; 3 static Result create(long const value) { 4 return Result{value}; 5 } 6 private: 7 explicit Result(long const value) : value(value) { } 8 }; 9 10 class Calculator {11 std::unordered_map<long, Result> cache_;12 public:13 Result calculate(long const coeff) {14 if (cache_.find(coeff) == cache_.end()) {15 Result r = Result::create(42); // ...16 cache_.insert(std::make_pair(coeff, r));17 }18 return cache_.find(coeff)->second;19 }20 };

Exposing classes1 bp::class_<Result>("Result", bp::no_init)2 .def_readonly("value", &Result::value);3 4 bp::class_<Calculator>("Calculator")5 .def("calculate", &Calculator::calculate);

1 from build import CppUsersWroclaw2 3 class WrappedCalculator(CppUsersWroclaw.Calculator):4 def calculate(self, coeff):5 print 'Calling original calculate...'6 return CppUsersWroclaw.Calculator.calculate(self, coeff)7 8 result = WrappedCalculator().calculate(69L)9 print result.value

szborows at probook in ~/D/p/b/code↪ python test2.pyCalling original calculate...42

Exposing classes

explicit exposuremember functions: d e fmember variables: d e f _ r e a d o n l y , d e f _ r e a d w r i t ec-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)

Inheritance35 struct Base {36 virtual ~Base() = default;37 virtual void virtualMethod() const {38 std::cout << "Base::virtualMethod\n";39 }40 41 void normalMethod() const {42 std::cout << "Base::normalMethod\n";43 }44 };45 46 struct Derived : Base {47 virtual void virtualMethod() const override {48 std::cout << "Derived::virtualMethod\n";49 }50 51 void normalMethod() const {52 std::cout << "Derived::normalMethod\n";53 }54 };55 56 Base * createDerived() { return new Derived; }

Inheritance65 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 }

1 from build.CppUsersWroclaw import *2 3 b = createDerived()4 5 b.virtualMethod()6 b.normalMethod()

szborows at probook in ~/D/p/b/code↪ python test3.pyDerived::virtualMethodBase::normalMethod

Special methods 7 template <typename T> 8 struct Vector { 9 Vector(T x, T y, T z)10 : x(x), y(y), z(z) { }11 12 T x = {}, y = {}, z = {};13 14 Vector & operator+=(Vector const& 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 Vector operator+(Vector const& other) {23 std::cout << "Vector::operator+\n";24 return Vector{25 x + other.x,26 y + other.y,27 z + other.z};28 }29 30 };

Special methods29 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 ;

1 from build.CppUsersWroclaw import *2 3 v1 = Vector(1.0, 2.0, 3.0)4 v2 = Vector(2.0, 3.0 ,4.0)5 6 v3 = v1 + v27 v2 += v1

szborows at probook in ~/D/p/b/code↪ python test4.pyVector::operator+Vector::operator+=

Special methods

C++ Pythono 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 60 constexpr auto FAMOUS_CONSTANT = 42; 61 62 enum class Role { 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 & enums1 from build.CppUsersWroclaw import *2 3 print FAMOUS_CONSTANT4 5 role = Role.ForgottenEmployee6 print role, '=', int(role)

szborows at probook in ~/D/p/b/code↪ python test5.py42ForgottenEmployee = 3

Boost.Python objects

Interchanging objects 1 from build.CppUsersWroclaw import * 2 3 x = [ 4 Vector(float(i), float(i), float(i)) 5 for i in range(10) 6 ] 7 8 x = list(reversed(x)) 9 10 for v in sortVectors(x):11 print v.x, v.y, v.z

Interchanging objects 73 bp::object sortVectors(bp::object vectors) { 74 bp::list list = bp::extract<bp::list>(vectors); 75 76 vector<Vector<float>> result; 77 for (size_t i = 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 [] (auto const& lhs, auto const& rhs) 83 { return lhs.x < rhs.x; }); 84 85 bp::list sorted; 86 for (auto const& e: result) { 87 sorted.append(e); 88 } 89 90 return sorted; 91 }

szborows at probook in ~/D/p/b/code↪ python test5.py0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.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 tl i s t b p : : l i s td i c t b p : : d i c ts t r b p : : s t rl o n g b p : : l o n g _* b p : : e x t r a c t < >collections → indexing suites custom types → converters

Embedding Python in C++ 9 int main() {10 Py_Initialize();11 auto mainModule = bp::import("__main__");12 auto mainNamespace = mainModule.attr("__dict__");13 14 mainNamespace["messageFromCpp"] = "I'm string from C++!"s;15 16 try {17 bp::exec(R"̂(18 print messageFromCpp19 response = 'Hello C++! I also support strings ;)'20 )̂", mainNamespace);21 string response = bp::extract<string>(mainNamespace["response"]);22 cout << response << endl;23 }24 catch(bp::error_already_set) {25 PyErr_Print();26 }27 }

szborows at probook in ~/D/p/b/code↪ ./embedded_pythonI'm string from C++!Hello C++! I also support strings ;)

Q & A