Mapping C ++ functions returning a pointer using Boost.Python

I want to show the following C ++ function for Python using Boost.Python:

int* test1() { return new int(42); } // Now exposing the function with Boost.Python BOOST_PYTHON_MODULE(libtest1) { using namespace boost::python; def("test1", test1); } 

When I try to compile this library, an error occurs because (this is my guess) Boost.Python does not know how to convert int * to PyObject.

I think what needs to be done to determine the transformation structure, something like this:

 template<class T> struct int_ptr_to_python { static PyObject* convert(int* i_ptr) { return i_ptr; } }; 

And pass this ad to BOOST_PYTHON_MODULE:

 BOOST_PYTHON_MODULE(libtest1) { using namespace boost::python; def("test1", test1); to_python_converter<int*, int_ptr_to_python<int*> >(); } 

But it also does not work. And I cannot find any information on how to handle the functions that pointers handle.

Does anyone help?

+8
c ++ python boost boost-python
source share
1 answer

In short, you cannot directly expose a function that returns int* using Boost.Python, since Python does not have a meaningful matching type. Integers are immutable.

  • If the goal is to return a number in Python, return int by value. This may require the use of a helper function to adapt legacy APIs.
  • If the goal is to return a pointer to the object highlighted by the new expression, then the object must be a class or union, and the function must be subject to certain policies to indicate owner responsibility.

Instead of immediately suggesting a final solution, I would like to spend time to execute compiler errors. With Boost.Python, sometimes pre-C ++ 11 static statements are used to provide instructions in compiler messages. Unfortunately, they are hard to find among heavy patterns.

The following code:

 #include <boost/python.hpp> int* make_int() { return new int(42); } BOOST_PYTHON_MODULE(example) { namespace python = boost::python; python::def("make_int", &make_int); } 

produces the following output in clang, with important details in bold:

 ... / boost / python / detail / caller.hpp: 102: 98: error:
   no member named 'get_pytype' in 'boost :: python :: detail ::
     specify_a_return_value_policy_to_wrap_functions_returning <int *> '
   ... create_result_converter ((PyObject *) 0, (ResultConverter *) 0, 
                              (ResultConverter *) 0) .g ...

Boost.Python tells us that for functions that return int* , you must specify boost::python::return_value_policy . There are various models of ResultConverterGenerators . Often, policies are used to control the semantics of ownership or the life of the returned object. Since the function in the source code returns a new pointer directly to Python, boost::python::manage_new_object is suitable if the caller is expected to take responsibility for deleting the object.

Specifying an object management policy still fails:

 #include <boost/python.hpp> int* make_int() { return new int(42); } BOOST_PYTHON_MODULE(example) { namespace python = boost::python; python::def("make_int", &make_int, python::return_value_policy<python::manage_new_object>()); } 

outputs the following result:

 ... / boost / python / object / make_instance.hpp: 27: 9:
   error: no matching function for call to 'assertion_failed'
     BOOST_MPL_ASSERT ((mpl :: or_ <is_class <T>, is_union <T>>)) ;

In this case, Boost.Python tells us that the object returned by the function with managed_new_object ResultConverterGenerator must be either class or union . For int* most appropriate solution is to return int by value when passing through the Boost.Python level. However, for completeness, the following is shown:

  • Using a helper function to adapt an obsolete API.
  • How to limit the creation of a user-defined type using the factory function, which returns pointers.
 #include <boost/python.hpp> /// Legacy API. int* make_int() { return new int(42); } /// Auxiliary function that adapts the legacy API to Python. int py_make_int() { std::auto_ptr<int> ptr(make_int()); return *ptr; } /// Auxiliary class that adapts the legacy API to Python. class holder : private boost::noncopyable { public: holder() : value_(make_int()) {} int get_value() const { return *value_; } void set_value(int value) { *value_ = value; } private: std::auto_ptr<int> value_; }; /// Factory function for the holder class. holder* make_holder() { return new holder(); } BOOST_PYTHON_MODULE(example) { namespace python = boost::python; python::def("make_int", &py_make_int); python::class_<holder, boost::noncopyable>("Holder", python::no_init) .add_property("value", python::make_function(&holder::get_value), python::make_function(&holder::set_value)) ; python::def("make_holder", &make_holder, python::return_value_policy<python::manage_new_object>()); } 

Interactive use:

 >>> import example >>> assert(42 == example.make_int()) >>> holder = example.Holder() Traceback (most recent call last): File "<stdin>", line 1, in <module> RuntimeError: This class cannot be instantiated from Python >>> holder = example.make_holder() >>> assert(42 == holder.value) >>> holder.value *= 2 >>> assert(84 == holder.value) 
+11
source share

All Articles