====== Boost.Pythonを使ってみる ====== 作成日: 2023-08-08 (火) [[https://youtu.be/_X_A6lev4fQ|第5回 Boost.Pythonを使ってみる]] プロジェクトレイアウト . └── extending-demos/ ├── Jamroot ├── simple/ │ ├── Jamfile │ ├── simple.cpp │ └── test_simple.py ├── point/ │ ├── Jamfile │ ├── point.cpp │ └── test_point.py ├── pypoint/ │ ├── Jamfile │ ├── pypoint.cpp │ └── test_pypoint.py └── fakegfx/ ├── Jamfile ├── fakegfx.h ├── fakegfx.cpp └── test_fakegfx.py ===== Jamroot ===== import python ; lib boost_python3 ; project : requirements boost_python3 ; build-project simple ; build-project point ; build-project pypoint ; build-project fakegfx ; ===== プロジェクト simple ===== ==== simple.cpp ==== #include #include #include namespace { void println(std::string const &s) { std::cout << s << '\n'; } std::string to_upper(std::string const &s) { return boost::to_upper_copy(s); } } // ns anon BOOST_PYTHON_MODULE(simple) { namespace python = boost::python; python::def("println", println); python::def("to_upper", to_upper); } ==== test_simple.py ==== import simple simple.println("Hello, Boost.Python!") u = simple.to_upper("Hello, Boost.Python!") simple.println(u) ==== Jamfile ==== project simple : requirements . ; python-extension simple : simple.cpp ; ===== プロジェクト point ===== ==== point.cpp ==== #include namespace { struct Point { double x = 0.0; double y = 0.0; Point() = default; Point(double x, double y) : x{x}, y{y} {} }; void translate(Point &pt, double tx, double ty) { pt.x += tx; pt.y += ty; } } BOOST_PYTHON_MODULE(point) { using namespace boost::python; class_("Point") .def(init()) .def("translate", translate) .def_readwrite("x", &Point::x) .def_readwrite("y", &Point::y); def("translate", translate); } ==== test_point.py ==== import point pt = point.Point() print(f"{pt.x},{pt.y}") pt.translate(100, -200) print(f"{pt.x},{pt.y}") ==== Jamfile ==== project point : requirements . ; python-extension point : point.cpp ; ===== プロジェクト pypoint ===== ==== pypoint.cpp ==== #include namespace { void translate(boost::python::object pt, double tx, double ty) { pt.attr("x") += tx; pt.attr("y") += ty; } } // ns anon BOOST_PYTHON_MODULE(pypoint) { boost::python::def("translate", translate); } ==== test_pypoint.py ==== import pypoint class Point: def __init__(self, x, y): self.x = x self.y = y pt = Point(1, 2) pypoint.translate(pt, 100, -200) print(f"{pt.x},{pt.y}") ==== Jamfile ==== project pypoint : requirements . ; python-extension pypoint : pypoint.cpp ; ===== プロジェクト fakegfx ===== ==== fakegfx.h ==== #ifndef FAKEGFX_H #define FAKEGFX_H #include struct Point { Point() = default; Point(double x, double y) : x{x}, y{y} {} double x, y; }; inline void translate(Point &pt, double tx, double ty) { pt.x += tx; pt.y += ty; } class Shape { public: virtual ~Shape() = default; virtual void draw() const = 0; }; class RectangleShape : public Shape { public: RectangleShape(Point const &position) : position_{position} {} void draw() const override { std::cout << "▬" << std::endl; } private: Point position_; }; inline void draw_shape(Shape const &shape) { shape.draw(); } inline void draw_rectangle_shape(RectangleShape const &shape) { shape.draw(); } #endif ==== fakegfx.cpp ==== #include "fakegfx.h" #include #include #include namespace { class ShapeWrap : public Shape, public boost::python::wrapper { public: void draw() const override { this->get_override("draw")(); } }; class RectangleShapeWrap : public RectangleShape , public boost::python::wrapper { public: RectangleShapeWrap(Point const &position) : RectangleShape{position} {} void draw() const override { if (boost::python::override draw = this->get_override("draw")) { draw(); } else { RectangleShape::draw(); } } void default_draw() const { std::cout << BOOST_CURRENT_FUNCTION << std::endl; RectangleShape::draw(); } }; } // ns anon BOOST_PYTHON_MODULE(fakegfx) { using namespace boost::python; class_("Point") .def(init()) .def("translate", &translate) .def_readwrite("x", &Point::x) .def_readwrite("y", &Point::y); class_("Shape") .def("draw", pure_virtual(&Shape::draw)); class_, boost::noncopyable>("RectangleShape", init()) .def("draw", &RectangleShape::draw /*1*/, &RectangleShapeWrap::default_draw /*2*/); // drawの呼び出しを、関数2に転送する // 関数2のシグネチャが関数1と一致することを保証させる // 関数1の指定はそのためだけに必要となる def("draw_shape", draw_shape); def("draw_rectangle_shape", draw_rectangle_shape); } ==== test_fakegfx.py ==== import fakegfx # some points pt = fakegfx.Point(1, 2) print(f"{pt.x}, {pt.y}") pt.translate(-100, 200) print(f"{pt.x}, {pt.y}") pt2 = fakegfx.Point() print(f"{pt2.x}, {pt2.y}") # circle shape # derived from C++ shape class # Shape <- CircleShape class CircleShape(fakegfx.Shape): def draw(self): #super().draw() # cause pure virtual function call print("○") circle = CircleShape() # rectangle shape # defined in C++ code rectangle = fakegfx.RectangleShape(pt) # square shape # derived from C++ rectangle shape class # Shape <- RectangleShape <- SquareSahpe class SquareShape(fakegfx.RectangleShape): def __init__(self): super().__init__(pt) def draw(self): fakegfx.RectangleShape.draw(self) # test non-cyclic print("▧") square = SquareShape() print("#circle#") fakegfx.draw_shape(circle) print("#rectangle#") fakegfx.draw_shape(rectangle) print("#square#") fakegfx.draw_shape(square) print("-circle-") circle.draw() print("-rectangle-") rectangle.draw() print("-square-") square.draw() ==== Jamfile ==== project extending : requirements . ; python-extension fakegfx : fakegfx.cpp ;