boost 的 python

boost 提供了一个简化书写 python module 的工具,即 boost.python。下面是一个简单的例子,

#include <boost/python.hpp>

struct world {
  std::string msg ;

  void
  set (const std::string& m) {
    msg = m ;
  }

  std::string
  greet () {
    return msg ;
  }
} ;

BOOST_PYTHON_MODULE(hello) {
  using namespace boost::python ;

  class_ ("world")
    .def ("greet", &world::greet)
    .def ("set", &world::set)
    ;
}

为了成功编译/连接这个程序,我们需要 include 对应 boost 和 python 的头文件(可能在 include/python2.6 里面),还需要链接到 libboost_python 和 libpython2.6 上(如果你的 boost.python 是 bind 在 python2.6 上的话)。之后我们获得的 so/dylib/dll 就可以被 python 所 import 了。我们可以看看展开后的对应部分是什么,

void init_module_hello ();

extern "C" __attribute__ ((visibility("default")))
void inithello() {
  boost::python::detail::init_module ("hello",&init_module_hello);
}

void init_module_hello() {
  using namespace boost::python ;

  class_ ("world")
    .def ("greet", &world::greet)
    .def ("set", &world::set)
    ;
}

比较前面的例子,这里的 external 大约就是 PyMODINIT_FUNC 展开的结果了,而我们的 boost::python::detail::init_module 传入的参数来看就是通过后面那个(C++)的函数定义了一个叫“hello”的 module。奇妙的可能就是为啥 class_ 这种写法能获得类似前面例子中 Py_InitModule 的作用了。我们先了解一下大致的接口,

  • class_ 用来 export 类(利用 PyTypeObject 实现),包括 def 输出成员函数,通过 init<…>模板输出的构造函数,add_property 增加对成员的封装(设定 setter 和 getter),class_ 模板第二个参数可以声明父类;
  • 为纯虚函数做 export 比较麻烦,首先得为原类增加 wrapper 类(继承该类和 wrapper<该类>),然后将 virtual 包一层,通过 wrapper 的 get_override 调用,之后 def 时使用 pure_virtual 表示是纯虚函数
  • 如果不是 pure virtual,需要提供默认实现,并在 def 时给出 wrapper 的 binding 和 default 实现;
  • 如果希望 export operator,可以用 def(expression) 来表示,其中用 self 表示本类;这包括一些 __xxx__ 的函数,如 abs 等

比较麻烦的是返回不受 python 管理内存的对象。对函数来说可以用 BOOST_PYTHON_FUNCTION_OVERLOADS。比较神奇的是下面这种搞法,

object f(object x, object y) {
  if (y == "foo")
    x.slice(3,7) = "bar";
  else
    x.attr("items") += y(3, x);
  return x;
}

object getfunc() {
  return object(f);
}

山寨的 python -,-b 更有甚者山寨了 tuple、list、dict、str、long_ 等。这个 object 是可以用来管理 PyObject 的。另外 boost.python 还提供了对 python 的 iterator 的封装,我们可以将一些 C++ 的 iterator 很容易的转换成为 python 的(使用 get_iterator 和 iterator 对象),如果某些成员需要弄成 iterator 可以使用 range 方法。

下面我们讨论如何利用一些对 python 本身的 API 的 hack 与 boost.python 协同工作。基本的做法就是将一些原先正常的函数一般如下

static PyObject*
some_func (PyObject* self, PyObject* args) {
  // first analyze input
  // ...
  // return something
  return blah ;
}

使用 boost.python 可以使用下面策略

static boost::python::object
some_func (boost::python::object o) {
  // convert to PyObject
  const PyObject* p = o.ptr () ;
  // doing things like before
  // ...
  // convert back to object
  return boost::python::object (boost::python::handle<>(boost::python::borrowed(pyobj))) ;
}

之后使用 boost.python 的方式 export 即可。

——————
And Abraham was ninety years old and nine, when he was circumcised in the flesh of his foreskin.

Advertisements
boost 的 python

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s