问题
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 PythonTypeError
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::object
s 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