如何将C ++对象转换为boost :: python :: object?

时间:2015-03-18 10:04:26

标签: c++ boost-python

bp :: extract将bp :: object转换为特定类型。 问题是怎么做副歌?

我们假设我有一个PointContainer和Point类。 我需要一个具有这种签名的功能

bp::object get_point(const PointContainer &, const bp::object & input);

应检查输入参数是否为整数。在这种情况下,它返回PointContainer中具有相应索引的Point实例的引用。 如果它不是整数,那么函数检查输入是否是切片对象(例如mylist [1:10:2])。在这种情况下,它返回PointContainer的副本。

问题是如何将Point,PointContainer实例转换为bp :: objects?

有关上述课程的一些细节

class_<Point<int>>("Point")
    .def("__getitem__", get_point_item)
    .def("__setitem__", set_point_item)
    .def("__len__", get_point_size)
    .def("__str__", print_point)
    .def("__eq__", &Point<int>::operator ==)
    .def("__ne__", &Point<int>::operator !=)
    .def("set_x", &Point<int>::set_x)
    .def("get_x", &Point<int>::get_x)
    .def("set_y", &Point<int>::set_y)
    .def("get_y", &Point<int>::get_y)
;

typedef std::vector<Point<int>> PointContainer;
typedef boost::shared_ptr<PointContainer> PointContainerPtr;

class_<PointContainer, PointContainerPtr>("PointContainer")
    .def("__iter__", iterator<PointContainer>())
    .def("__getitem__", get_point)
    .def("__setitem__", set_point)
    .def("__len__", &PointContainer::size)
    .def("append", push_point)
    .def("reserve", &PointContainer::reserve)
    .def("clear", &PointContainer::clear)
;

2 个答案:

答案 0 :(得分:5)

对于通过boost::python::class_公开类型的C ++对象,可以使用以下constructor构建一个带有C ++对象实例的Python对象:

template <class T>
explicit object(T const& x);
     

效果:将x转换为python并管理对它的引用。

     

抛出: error_already_set如果无法进行此类转换,则会设置Python TypeError异常。


当通过boost::python::class_公开某个类型时,Boost.Python将为C ++类型注册-python和from-python转换器。当使用object()的模板化构造函数时,它将检查内部注册表中的to-python转换器并在找到时使用它。生成的Python对象将拥有并拥有自己的C ++对象实例。

以下是从C ++对象构建boost::python::object的完整最小示例demonstrating

#include <boost/python.hpp>

// Mockup types.
class spam {};
class egg  {};

// Factory function that returns boost::python::objects.
boost::python::object make_object(std::string name)
{
  namespace python = boost::python;
  if (name == "spam")     return python::object(spam{});
  else if (name == "egg") return python::object(egg{});
  else return python::object();
}

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;

  // Expose models.
  python::class_<spam>("Spam", python::init<>());
  python::class_<egg>("Egg", python::init<>());

  // Expose factory function.
  python::def("make_object", &make_object);
}

交互式使用:

>>> import example
>>> assert(type(example.make_object("spam")) is example.Spam)
>>> assert(type(example.make_object("egg")) is example.Egg)
>>> assert(example.make_object("bogus") is None)

如果需要不同的返回值语义,例如boost::python::object应该引用现有的C ++对象而不是副本,那么在包装C ++函数时需要提供call policies

答案 1 :(得分:0)

诀窍是使用 g_python_point_container_class 变量来实例化其包装的PointContainer类实例,以便能够使用bp :: object处理它。 提到的全局变量在declare_pt_container()函数中初始化。

P.S。拥有全局对象是C ++中的禁忌。为简单起见,我在这个样本中做到了。

    bp::object g_python_point_container_class;

    bp::object get_point(const PointContainer & points, const bp::object & input) {
        bp::extract<bp::slice> slice_extractor(input);
        if (slice_extractor.check()) {
            const bp::slice & slice = slice_extractor;
            if (slice.start().is_none() &&
                slice.stop().is_none() &&
                slice.step().is_none()) {
                // copy entire container
                bp::object obj = g_python_point_container_class(points);
                return obj;
            }
            else {
                throw ExceptionBaseError("random slicing is not implemented");
            }
        }
        bp::extract<int> int_extractor(input);
        if (int_extractor.check()) {        
            // convert Point to bp::object in the same way using
            // extracted integer as an index ....
        }
        throw ExceptionTypeError("only integer or slice object is expected");
    }

    PointContainerPtr point_container_constructor_empty() {
        return PointContainerPtr(boost::make_shared<PointContainer>());
    }

    PointContainerPtr point_container_constructor_copy(
                                        const PointContainer & pt_container) {
        return PointContainerPtr(boost::make_shared<PointContainer>(pt_container));
    }

    void declare_pt_container() {
            g_python_point_container_class =
                class_<PointContainer, PointContainerPtr>("PointContainer", no_init)
                .def("__init__", make_constructor(point_container_constructor_empty))
                .def("__init__", make_constructor(point_container_constructor_copy))
                .def("__iter__", iterator<PointContainer>())
                .def("__getitem__", get_point)
                .def("__setitem__", set_point)
                .def("__len__", &PointContainer::size)
                .def("append", push_point)
                .def("reserve", &PointContainer::reserve)
                .def("clear", &PointContainer::clear)
            ;
        }

BOOST_PYTHON_MODULE(PythonModuleName) {
    declare_pt_container(); 
}