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)