boost.pythonで学んだあれこれ

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

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

やりたいこと

```pycon
>>> import hoge
>>> hoge.GLOBAL_VALUE
1
```

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

```cpp
BOOST_PYTHON_MODULE(hoge)
{
    boost::python::scope().attr("GLOBAL_VALUE") = 1;
}
```

## `__str__`, `__repr__`を定義する

やりたいこと

```pycon
>>> import hoge
>>> h = hoge.hoge()
>>> print h
hoge
>>> h
hoge
```

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

```cpp
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のメンバにアクセスする

やりたいこと

```pycon
>>> import hoge
>>> f = hoge.foo()
>>> f.a
0
>>> f.a = 5
>>> f.a
5
```

`def_readwrite`を使います。

```cpp
struct foo
{
    int a;
    foo(): a(0){};
};

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

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

やりたいこと

```pycon
>>> 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に設定しています。

```cpp
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());
}
```

## 上の例を可変引数でやる

やりたいこと

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

```cpp
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::ptime`を`datetime`に変換

やりたいこと

```pycon
>>> 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)
```

```cpp
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`を持たせたい
やりたいこと

```pycon
>>> 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`を使います。

```cpp
// 上の例で使った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`を扱う
やりたいこと

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

`vector_indexing_suite`を使います。

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

## `std::map`を使う
やりたいこと

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

`map_indexing_suite`を使います。

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