【问题标题】:wrap boost::optional using boost::python使用 boost::python 包装 boost::optional
【发布时间】:2016-04-07 19:48:38
【问题描述】:

有没有办法包装boost::optional<T> 类型对象以通过boost::python::class_ 公开它(从BOOST_PYTHON_MODULE 使用)

struct Foo 
{
    boost::optional<int> bar;
};

BOOST_PYTHON_MODULE(module_name)
{
    class_<Foo>("Foo")
    .def_readwrite("bar", &Foo::bar);
}

在这种情况下,我对 Python 的期望是 AttributeError

import module_name
f = module_name.Foo()
print f.bar

因为bar 的值尚未设置。 而TypeError

import module_name
f = module_name.Foo()
f.bar = "string"

bar 属于 int 类型。

另一个相关的问题是以同样的方式导出boost::python::indexing_suite容器类型的类对象。

使用boost::python api可以解决问题吗?

【问题讨论】:

  • this question。也许如果它需要一个函数才能工作,那么使用 setter/getter 包装它。

标签: python c++ boost boost-python boost-optional


【解决方案1】:

您需要exception translatorpython converters

异常翻译器

namespace bp = boost::python;

// Custom exceptions
struct AttributeError: std::exception
{
  const char* what() const throw() { return "AttributeError exception"; }
};

struct TypeError: std::exception
{
  const char* what() const throw() { return "TypeError exception"; }
};

// Set python exceptions
void translate(const std::exception& e)
{
  if(dynamic_cast<const AttributeError*>(&e)) 
     PyErr_SetString(PyExc_AttributeError, e.what());
  if(dynamic_cast<const TypeError*>(&e)) 
     PyErr_SetString(PyExc_TypeError, e.what());
}

BOOST_PYTHON_MODULE(module_name)
{
  // Exception translator
  bp::register_exception_translator<AttributeError>(&translate);
  bp::register_exception_translator<TypeError>(&translate);
  ...
}

转 python 转换器

template <typename T>
struct to_python_optional
{
  static PyObject* convert(const boost::optional<T>& obj)
  {
    if(obj) return bp::incref(bp::object(*obj).ptr());
    // raise AttributeError if any value hasn't been set yet
    else throw AttributeError();
  }
};

BOOST_PYTHON_MODULE(module_name)
{
  ... 
  bp::to_python_converter<boost::optional<int>,
                          to_python_optional<int> >();
  ...
}

From-python 转换器

template<typename T>
struct from_python_optional
{ 
  static void* convertible(PyObject *obj_ptr)
    {
      try { return typename bp::extract<T>::extract(obj_ptr) ? obj_ptr : 0 ; }
      // Without try catch it still raises a TypeError exception
      // But this enables to custom your error message
      catch(...) { throw TypeError(); }
    }

  static void construct(
    PyObject *obj_ptr,
    boost::python::converter::rvalue_from_python_stage1_data* data)
    {
      const T value = typename bp::extract<T>::extract(obj_ptr);

      assert(value);

      void* storage = (
        (bp::converter::rvalue_from_python_storage<boost::optional<T> >*)
        data)->storage.bytes;

      new (storage) boost::optional<T>(value);

      data->convertible = storage;
    }

  from_python_optional()
  {
    bp::converter::registry::push_back(
      &convertible,
      &construct,
      bp::type_id<boost::optional<T> >());
  }
};

BOOST_PYTHON_MODULE(module_name)
{
  ... 
  from_python_optional<int>();
  ...
}

而且你不能用def_readwrite(see this FAQ)使用转换器,你必须使用add_property

BOOST_PYTHON_MODULE(module_name)
{
  ...
  bp::class_<Foo>("Foo")
    .add_property("bar", bp::make_getter(&Foo::bar,
                         bp::return_value_policy<bp::return_by_value>()),
                         bp::make_setter(&Foo::bar,
                         bp::return_value_policy<bp::return_by_value>()));
}

因此,您将在 Python 解释器中获得这些输出:

>>> import module_name
>>> f = module_name.Foo()
>>> print f.bar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: AttributeError exception
>>> f.bar="string"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: TypeError exception

【讨论】:

  • 谢谢! construct 函数中的 data 指针参数是指向实际存储成员变量 Foo::bar 的内存的指针吗?如果是这样,那么为什么在new 之前没有检查它是否尚未使用?另一个问题是关于return_value_policy: 1. 为什么我们要为返回无效的setter 指定任何返回策略? 2. 为什么是return_by_value
  • data 指针保存转换结果的地址(python 整数)。 Boost 文档在这个问题上真的很差,所以我们必须查看源文件。在rvalue_from_python_data.hpp中,我们可以看到rvalue_from_python_storage使用boost::python::detail::referent_storageboost::add_reference来获取内存块。
  • 你也可以去add_reference.hppreferent_storage.hpp看看。
  • 确实,return_value_policy 没有必要指定设置器(我刚刚检查过)。我盲目地遵循了Boost的FAQ:P
  • this link 你会发现不同的退货政策。我选择return_by_value 是因为我了解您想要获得价值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-04-09
  • 1970-01-01
  • 2021-12-16
  • 1970-01-01
  • 2014-11-21
  • 2015-12-01
相关资源
最近更新 更多