C++/Python: Boost.Python でカスタムコンバーターを作る
C++ のクラスを Python で扱うとき、std::pair などのクラスはデフォルトでは型変換が行われない。自分でコンバーターを書く必要がある。
以下はコードの例。C++11のラムダ式を利用したら綺麗にまとまった。
#include <boost/python.hpp>
#include "your_code.hpp"
namespace yourmodulename {
namespace py = boost::python;
/*
* Converter for result set (std::vector of std::pair => pylist of pytuple)
*/
namespace detail {
template <typename T, typename U>
struct pair_vector_to_tuple_list {
static PyObject* convert(std::vector<std::pair<T, U> > const& ps) {
py::list ret;
for (auto it: ps) ret.append(py::make_tuple(it.first, it.second));
return py::incref(ret.ptr());
}
static PyTypeObject const* get_pytype() { return &PyList_Type; }
};
}
template <typename T, typename U>
void expose_pair_vector_to_tuple_list() {
py::to_python_converter<std::vector<std::pair<T, U> >, detail::pair_vector_to_tuple_list<T, U>, true>();
}
/*
* Converter for list parameter (pylist => std::vector)
*/
template <typename T>
void expose_pylist_to_vector() {
typedef std::vector<T> VT;
auto convertible = [](PyObject* obj_ptr) -> void* { return PySequence_Check(obj_ptr) ? obj_ptr : NULL; };
auto construct = [](PyObject* obj_ptr, py::converter::rvalue_from_python_stage1_data* data) {
VT* storage = new (reinterpret_cast<py::converter::rvalue_from_python_storage<VT>*>(data)->storage.bytes) VT();
for (py::ssize_t i = 0, l = PySequence_Size(obj_ptr); i < l; ++i) {
storage->push_back(py::extract<typename boost::range_value<VT>::type>(PySequence_GetItem(obj_ptr, i)));
}
data->convertible = storage;
};
py::converter::registry::push_back(convertible, construct, py::type_id<VT>());
}
}
BOOST_PYTHON_MODULE(yourmodulename) {
using namespace boost::python;
using namespace yourmodulename;
// expose converters
expose_pair_vector_to_tuple_list<int, double>();
expose_pylist_to_vector<int>();
// expose classes
...
}
なかなかに魔力を吸い取られそうなコードだ。
Related Posts
- mog project: C++/Python: Diving Into Boost.Python
- mog project: C++/Python: How to run Boost.Python module using setup.py with Python3.4 on Mac