How to convert C++ objects to boost::python::object?

巧了我就是萌 提交于 2019-12-23 02:05:48

问题


bp::extract converts bp::object to specific type. The question is how to do vice-verse?

Let's presume I have a PointContainer and Point classes. I need to have a function with such signature

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

It should check if input argument is an integer. In that case it returns a reference to Point instance from PointContainer with corresponding index. If it is not an integer then function checks if the input is a slice object (ex. mylist[1:10:2]). In that case it returns a copy of PointContainer.

The question is how to convert Point, PointContainer instances to bp::objects?

Some details about mentioned classes

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)
;

回答1:


For C++ objects whose type has been exposed via boost::python::class_, one can construct a Python object with an instance of a C++ object using the following constructor:

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

Effects: converts x to python and manages a reference to it.

Throws: error_already_set and sets a Python TypeError exception if no such conversion is possible.


When a type is exposed via boost::python::class_, Boost.Python will register to-python and from-python converters for the C++ type. When the templated constructor for object() is used, it will check the internal registry for a to-python converter and use it if found. The resulting Python object will have and own its own instance of the C++ object.

Here is a complete minimal example demonstrating constructing boost::python::objects from C++ objects:

#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);
}

Interactive usage:

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

If different return value semantics are needed, such as the boost::python::object should have a reference to an existing C++ object rather than a copy, then one needs to provide call policies when wrapping C++ functions.




回答2:


The trick is to use g_python_point_container_class variable to instantiate its wrapped PointContainer class instance in order to be able to handle it with bp::object. The mentioned global variable is initialized in the declare_pt_container() function.

P.S. having global objects is taboo in C++. I did it in this sample for simplicity.

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


来源:https://stackoverflow.com/questions/29119086/how-to-convert-c-objects-to-boostpythonobject

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!