How to wrap a C ++ function that returns boost :: optional <T>?

I want to wrap a function that returns boost::optional<T> . That is, given:

 class Foo { boost::optional<T> func(); }; 

I would like to wrap this somehow, so that Python either gets the value T by value, or None :

 class_<Foo>("Foo") .def("func", func, return_value_policy<return_boost_optional???>); 

Usually, if he had just returned T , I could use:

 class_<Foo>("Foo") .def("func", func, return_value_policy<return_by_value>()); 

But since it returns boost::optional<T> , it can also return boost::none , which I would call Python None .

Is there a way to do this using existing converters? If not, is there a workaround to achieve the same effect?

+5
source share
1 answer

The ResultConverter concept is designed to solve this problem. return_value_policy The return_value_policy model uses a ResultConverterGenerator to create a ResultConverter, and a ResultConverter is used to change the return value of the function exposed by Python. In this case, a custom type that implements the concept of ResultConverter can be used to return Python None or instantiate an object with the corresponding Python class. Although the documentation lists all the requirements for a type, it may be easier to understand with something closer to the code:

 /// @brief The ResultConverterGenerator. struct result_converter_generator { template <typename T> struct apply { struct result_converter { // Must be default constructible. result_converter(); // Return true if T can be converted to a Python Object. bool convertible(); // Convert obj to a PyObject, explicitly managing proper reference // counts. PyObject* operator(const T& obj); // Returns the Python object that represents the type. Used for // documentation. const PyTypeObject* get_pytype(); }; /// @brief The ResultConverter. typedef result_converter type; }; }; 

Often when creating a custom ResultConverter model, you can use metaprogramming of templates to minimize the likelihood of errors at runtime in conversions and even catch errors at compile time and provide meaningful messages. Here is a complete example of return_optional , the ResultConverter model, which takes a C ++ boost::optional<T> object and converts it to the corresponding Python object.

 #include <boost/mpl/if.hpp> #include <boost/optional.hpp> #include <boost/python.hpp> #include <boost/type_traits/integral_constant.hpp> #include <boost/utility/in_place_factory.hpp> /// @brief Mockup model. class spam {}; /// @brief Mockup factory for model. boost::optional<spam> make_spam(bool x) { return x ? boost::optional<spam>(boost::in_place()) : boost::none; } namespace detail { /// @brief Type trait that determines if the provided type is /// a boost::optional. template <typename T> struct is_optional : boost::false_type {}; template <typename T> struct is_optional<boost::optional<T> > : boost::true_type {}; /// @brief Type used to provide meaningful compiler errors. template <typename> struct return_optional_requires_a_optional_return_type {}; /// @brief ResultConverter model that converts a boost::optional object to /// Python None if the object is empty (ie boost::none) or defers /// to Boost.Python to convert object to a Python object. template <typename T> struct to_python_optional { /// @brief Only supports converting Boost.Optional types. /// @note This is checked at runtime. bool convertible() const { return detail::is_optional<T>::value; } /// @brief Convert boost::optional object to Python None or a /// Boost.Python object. PyObject* operator()(const T& obj) const { namespace python = boost::python; python::object result = obj // If boost::optional has a value, then ? python::object(*obj) // defer to Boost.Python converter. : python::object(); // Otherwise, return Python None. // The python::object contains a handle which functions as // smart-pointer to the underlying PyObject. As it will go // out of scope, explicitly increment the PyObject reference // count, as the caller expects a non-borrowed (ie owned) reference. return python::incref(result.ptr()); } /// @brief Used for documentation. const PyTypeObject* get_pytype() const { return 0; } }; } // namespace detail /// @brief Converts a boost::optional to Python None if the object is /// equal to boost::none. Otherwise, defers to the registered /// type converter to returs a Boost.Python object. struct return_optional { template <class T> struct apply { // The to_python_optional ResultConverter only checks if T is convertible // at runtime. However, the following MPL branch cause a compile time // error if T is not a boost::optional by providing a type that is not a // ResultConverter model. typedef typename boost::mpl::if_< detail::is_optional<T>, detail::to_python_optional<T>, detail::return_optional_requires_a_optional_return_type<T> >::type type; }; // apply }; // return_optional BOOST_PYTHON_MODULE(example) { namespace python = boost::python; python::class_<spam>("Spam") ; python::def("make_spam", &make_spam, python::return_value_policy<return_optional>()); } 

Interactive use:

 >>> import example >>> assert(isinstance(example.make_spam(True), example.Spam)) >>> assert(example.make_spam(False) is None) 

Compilation time type checking occurs when the return_optional ResultConvert attempt is used with a function that returns a value that is not boost::optional . For example, when the following is used:

 struct egg {}; egg* make_egg(); BOOST_PYTHON_MODULE(example) { namespace python = boost::python; python::def("make_egg", &make_egg, python::return_value_policy<return_optional>()); } 

The compiler will choose return_optional_requires_a_optional_return_type and compile the failure. Here is the part of the clang compiler error message:

  error: no member named 'get_pytype' in
 ' detail :: return_optional_requires_a_optional_return_type <egg *> '
+10
source

Source: https://habr.com/ru/post/1205238/


All Articles