c++ - Python C API - How to construct object from PyObject -
i'm looking find out if there's nice, "native" way construct object given pyobject*
known type.
here code stands:
c++
void add_component(boost::python::object& type) { auto constructed_type = type(); // doesn't construct anything! }
python
o = gameobject() o.add_component(cameracomponent)
my code executing entire function fine, constructor never triggered cameracomponent
.
so question is, how i, given pyobject*
known a type, construct instance of type?
many in advance.
if boost::python::object
references type, invoking construct object referenced type:
boost::python::object type = /* py_type */; boost::python::object object = type(); // isinstance(object, type) == true
as in python object, accepting arguments python boost::python::object
allow type of object, not type. long object callable (__call___
), code succeed.
on other hand, if want guarantee type provided, 1 solution create c++ type represents python type, accept argument, , use custom converter construct c++ type if python type provided.
the following type_object
c++ type represents python object py_type
.
/// @brief boost::python::object refers type. struct type_object: public boost::python::object { /// @brief if object type, refer it. otherwise, /// refer instance's type. explicit type_object(boost::python::object object): boost::python::object(object) { if (!pytype_check(object.ptr())) { throw std::invalid_argument("type_object requires python type"); } } }; ... // accepts python type. void add_component(type_object type) { ... }
the following custom converter construct type_object
instance if provided pyobject*
py_type
:
/// @brief enable automatic conversions type_object. struct enable_type_object { enable_type_object() { boost::python::converter::registry::push_back( &convertible, &construct, boost::python::type_id<type_object>()); } static void* convertible(pyobject* object) { return pytype_check(object) ? object : null; } static void construct( pyobject* object, boost::python::converter::rvalue_from_python_stage1_data* data) { // obtain handle memory block converter has allocated // c++ type. namespace python = boost::python; typedef python::converter::rvalue_from_python_storage<type_object> storage_type; void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes; // construct type object within storage. object borrowed // reference, create handle indicting borrowed proper // reference counting. python::handle<> handle(python::borrowed(object)); new (storage) type_object(python::object(handle)); // set convertible indicate success. data->convertible = storage; } }; ... boost_python_module(...) { enable_type_object(); // register type_object converter. }
here complete example demonstrating exposing function requires python type, constructs instance of type:
#include <iostream> #include <stdexcept> // std::invalid_argument #include <boost/python.hpp> /// @brief boost::python::object refers type. struct type_object: public boost::python::object { /// @brief if object type, refer it. otherwise, /// refer instance's type. explicit type_object(boost::python::object object): boost::python::object(object) { if (!pytype_check(object.ptr())) { throw std::invalid_argument("type_object requires python type"); } } }; /// @brief enable automatic conversions type_object. struct enable_type_object { enable_type_object() { boost::python::converter::registry::push_back( &convertible, &construct, boost::python::type_id<type_object>()); } static void* convertible(pyobject* object) { return pytype_check(object) ? object : null; } static void construct( pyobject* object, boost::python::converter::rvalue_from_python_stage1_data* data) { // obtain handle memory block converter has allocated // c++ type. namespace python = boost::python; typedef python::converter::rvalue_from_python_storage<type_object> storage_type; void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes; // construct type object within storage. object borrowed // reference, create handle indicting borrowed proper // reference counting. python::handle<> handle(python::borrowed(object)); new (storage) type_object(python::object(handle)); // set convertible indicate success. data->convertible = storage; } }; // mock api. struct gameobject {}; struct cameracomponent { cameracomponent() { std::cout << "cameracomponent()" << std::endl; } }; boost::python::object add_component(gameobject& /* self */, type_object type) { auto constructed_type = type(); return constructed_type; } boost_python_module(example) { namespace python = boost::python; // enable receiving type_object arguments. enable_type_object(); python::class_<gameobject>("gameobject") .def("add_component", &add_component); python::class_<cameracomponent>("cameracomponent"); }
interactive usage:
>>> import example >>> game = example.gameobject() >>> component = game.add_component(example.cameracomponent) cameracomponent() >>> assert(isinstance(component, example.cameracomponent)) >>> try: ... game.add_component(component) # throws boost.python.argumenterror ... assert(false) ... except typeerror: ... assert(true) ...
Comments
Post a Comment