繁体   English   中英

如何包装返回boost :: optional的C ++函数 <T> ?

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

我想包装一个返回boost::optional<T>的函数。 那是,给定:

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

我想以某种方式包装它,以便Python获得T值或者None

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

通常如果它刚刚返回T ,我可以使用:

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

但是因为它返回一个boost::optional<T> ,它也可以返回boost::none ,我想最终得到Python的None

有没有办法用现有的转换器做到这一点? 如果没有,是否有一些解决方法可以达到同样的效果?

ResultConverter概念旨在解决此问题。 return_value_policy CallPolicy模型使用ResultConverterGenerator创建ResultConverter,ResultConverter用于修改暴露给Python的函数的返回值。 在这种情况下,实现ResultConverter概念的自定义类型可用于返回Python None或使用适当的Python类实例化对象。 虽然文档列出了所有类型的要求,但使用更接近类似代码的东西可能更容易理解:

/// @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;
  };
};

通常,在创建自定义ResultConverter模型时,可以使用模板元编程来最小化转换中运行时错误的可能性,甚至可以在编译时捕获错误并提供有意义的消息。 下面是return_optional的完整示例,这是一个ResultConverter模型,它接受C ++ boost::optional<T>对象,并将其转换为适当的Python对象。

#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 (i.e. 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's reference
    // count, as the caller expects a non-borrowed (i.e. 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>());
}

互动用法:

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

当尝试将return_optional ResultConvert与返回不是boost::optional的值的函数一起使用时,会发生编译时类型检查。 例如,使用以下内容时:

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>());
}

编译器将选择return_optional_requires_a_optional_return_type实现,并且编译失败。 以下是clang提供的编译器错误消息的一部分:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM