Posted at

boost.pythonで学んだあれこれ

More than 3 years have passed since last update.


boost.pythonで学んだあれこれ

boost.pythonを使ってみてあれこれ学んだことを書いてみたいと思います。


モジュールのグローバル変数を追加する

やりたいこと

>>> import hoge

>>> hoge.GLOBAL_VALUE
1

boost::python::scopeを使います。

BOOST_PYTHON_MODULE(hoge)

{
boost::python::scope().attr("GLOBAL_VALUE") = 1;
}


__str__, __repr__を定義する

やりたいこと

>>> import hoge

>>> h = hoge.hoge()
>>> print h
hoge
>>> h
hoge

いろいろやり方はあると思いますが、std::ostreamからpythonの__str__, __repr__に変換する方法です。

class hoge

{
};

std::ostream& operator<<(std::ostream& out, const hoge& )
{
out << "hoge";
return out;
}

BOOST_PYTHON_MODULE(hoge)
{
using namespace boost::python;
class_<hoge>("hoge")
.def(self_ns::str(self))
.def(self_ns::repr(self));
}


structのメンバにアクセスする

やりたいこと

>>> import hoge

>>> f = hoge.foo()
>>> f.a
0
>>> f.a = 5
>>> f.a
5

def_readwriteを使います。

struct foo

{
int a;
foo(): a(0){};
};

BOOST_PYTHON_MODULE(hoge)
{
using namespace boost::python;
class_<foo>("foo")
.def_readwrite("a", &foo::a);
}


関数のデフォルト引数を使う

やりたいこと

>>> import hoge

>>> hoge.add(1)
1
>>> hoge.add(1, 2)
3
>>> hoge.add(1, 2, 3)
6

BOOST_PYTHON_FUNCTION_OVERLOADSとかBOOST_PYTHON_MEMBER_FUNCTION_OVERLOADSを使います。

BOOST_PYTHON_FUNCTION_OVERLOADS(add_overloads, add, 1, 3)addの最小引数の数を1、最大引数の数を3に設定しています。

int add(int a, int b = 0, int c = 0)

{
return a + b + c;
}

BOOST_PYTHON_FUNCTION_OVERLOADS(add_overloads, add, 1, 3)

BOOST_PYTHON_MODULE(hoge)
{
using namespace boost::python;
def("add", add, add_overloads());
}


上の例を可変引数でやる

やりたいこと

>>> import hoge

>>> hoge.add(1, 2, 3, a = 4, c = 5)
>>> 15

using namespace boost::python;

int add(tuple args, dict kw)
{
int sum = 0;
for (int i = 0; i < len(args); ++i)
{
sum += extract<int>(args[i]);
}
list vals = kw.values();
for (int i = 0; i < len(vals); ++i)
{
sum += extract<int>(vals[i]);
}
return sum;
}

BOOST_PYTHON_MODULE(hoge)
{
def("add", raw_function(add));
}


boost::posix_time::ptimedatetimeに変換

やりたいこと

>>> import hoge

>>> f = hoge.foo()
>>> f.now()
datetime.datetime(2015, 12, 30, 15, 29, 12)
>>> f.now()
datetime.datetime(2015, 12, 30, 15, 29, 14)

static long get_usecs(boost::posix_time::time_duration const& d)

{
static long resolution
= boost::posix_time::time_duration::ticks_per_second();
long fracsecs = d.fractional_seconds();
if (resolution > 1000000)
return fracsecs / (resolution / 1000000);
else
return fracsecs * (1000000 / resolution);
}

struct ptime_to_python_datetime
{
static PyObject* convert(boost::posix_time::ptime const& pt)
{
boost::gregorian::date date = pt.date();
boost::posix_time::time_duration td = pt.time_of_day();
return PyDateTime_FromDateAndTime((int)date.year(),
(int)date.month(),
(int)date.day(),
td.hours(),
td.minutes(),
td.seconds(),
get_usecs(td));
}
};

class foo
{
public:
boost::posix_time::ptime now()
{
return boost::posix_time::second_clock::local_time();
}
};

BOOST_PYTHON_MODULE(hoge)
{
using namespace boost::python;
PyDateTime_IMPORT;

to_python_converter<const boost::posix_time::ptime, ptime_to_python_datetime>();

class_<foo>("foo")
.def("now", &foo::now);
}


structにdatetimeを持たせたい

やりたいこと

>>> import hoge

>>> import datetime
>>> h = hoge.hoge()
>>> h.date = datetime.datetime.now()
>>> h.date
datetime.datetime(2015, 12, 30, 15, 40, 6, 406588)

さっき使ったdef_readwriteは使えなくて、add_propertyを使います。

// 上の例で使ったptime_to_python_datetimeは同様に書いておく。

struct ptime_from_python_datetime
{
ptime_from_python_datetime()
{
boost::python::converter::registry::push_back(&convertible,
&construct,
boost::python::type_id<boost::posix_time::ptime>());
}

static void* convertible(PyObject * obj_ptr)
{
if (!PyDateTime_Check(obj_ptr))
return 0;
return obj_ptr;
}

static void construct(PyObject* obj_ptr,
boost::python::converter::rvalue_from_python_stage1_data * data)
{
PyDateTime_DateTime const* pydate
= reinterpret_cast<PyDateTime_DateTime*>(obj_ptr);

// Create date object
boost::gregorian::date _date(PyDateTime_GET_YEAR(pydate),
PyDateTime_GET_MONTH(pydate),
PyDateTime_GET_DAY(pydate));

// Create time duration object
boost::posix_time::time_duration
_duration(PyDateTime_DATE_GET_HOUR(pydate),
PyDateTime_DATE_GET_MINUTE(pydate),
PyDateTime_DATE_GET_SECOND(pydate),
0);
// Set the usecs value
_duration += boost::posix_time::microseconds(PyDateTime_DATE_GET_MICROSECOND(pydate));

// Create posix time object
void* storage = ((boost::python::converter::rvalue_from_python_storage<boost::posix_time::ptime>*)
data)->storage.bytes;
new(storage) boost::posix_time::ptime(_date, _duration);
data->convertible = storage;
}
};

struct hoge
{
boost::posix_time::ptime date;
};

BOOST_PYTHON_MODULE(hoge)
{
using namespace boost::python;
PyDateTime_IMPORT;

ptime_from_python_datetime();
to_python_converter<const boost::posix_time::ptime, ptime_to_python_datetime>();

class_<hoge>("hoge")
.add_property("date",
make_getter(&hoge::date, return_value_policy<return_by_value>()),
make_setter(&hoge::date, return_value_policy<copy_non_const_reference>()));

}


std::vectorを扱う

やりたいこと

>>> import hoge

>>> v = hoge.DoubleVector()
>>> v.append(1.0)
>>> v.append(2.0)
>>> v[0]
1.0
>>> v[1]
2.0

vector_indexing_suiteを使います。

BOOST_PYTHON_MODULE(hoge)

{
using namespace boost::python;
class_<std::vector<double> >("DoubleVector")
.def(vector_indexing_suite<std::vector<double> >());
}


std::mapを使う

やりたいこと

>>> import hoge

>>> m = hoge.StringDoubleMap()
>>> m["a"] = 1
>>> m["b"] = 2
>>> m["a"]
1.0
>>> m["b"]
2.0

map_indexing_suiteを使います。

BOOST_PYTHON_MODULE(hoge)

{
using namespace boost::python;
class_<std::map<std::string, double> >("StringDoubleMap")
.def(map_indexing_suite<std::map<std::string, double> >());
}