====== 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 ;