如何包装一个C ++函数返回boost :: optional< T>? [英] How to wrap a C++ function that returns boost::optional<T>?

查看:539
本文介绍了如何包装一个C ++函数返回boost :: optional< T>?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

我想要换一种方式,让Python获得 T 按值或

  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的



有没有办法用现有的转换器?如果没有,是否有一些解决方法来实现相同的效果?

解决方案

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

  /// @brief ResultConverterGenerator。 
struct result_converter_generator
{
template< typename T>
struct apply
{
struct result_converter
{
//必须是默认可构造的。
result_converter();

//如果T可以转换为Python对象,则返回true。
bool convertible()

//将obj转换为PyObject,显式管理正确的引用
//计数。
PyObject * operator(const T& obj);

//返回表示类型的Python对象。用于
//文档。
const PyTypeObject * get_pytype();
};

/// @brief ResultConverter。
typedef result_converter type;
};
};

通常,当创建一个自定义的ResultConverter模型时,可以使用模板元程序来最小化机会的转换中的运行时错误,甚至在编译时捕获错误并提供有意义的消息。这是一个使用C ++ boost :: optional< T> 对象的ResultConverter模型的 return_optional 并将其转换为适当的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模型。
class spam {};

/// @brief Mockup工厂的模型。
boost :: optional< spam> make_spam(bool x)
{
return x? boost :: optional< spam>(boost :: in_place()):boost :: none;
}

命名空间详细信息{

/// @brief键入trait,确定提供的类型是否为
/// boost :: optional 。
template< typename T>
struct is_optional:boost :: false_type {};

template< typename T>
struct is_optional< boost :: optional< T> > :boost :: true_type {};

/// @brief用来提供有意义的编译器错误的类型。
template< typename>
struct return_optional_requires_a_optional_return_type {};

/// @brief ResultConverter将boost :: optional对象转换为
的模型/// Python如果对象为空(即boost :: none)或defers,
///到Boost.Python将对象转换为Python对象。
template< typename T>
struct to_python_optional
{
/// @brief只支持转换Boost.Optional类型。
/// @note这是在运行时检查。
bool convertible()const {return detail :: is_optional< T> :: value; }

/// @brief将boost :: optional对象转换为Python None或
/// Boost.Python对象。
PyObject * operator()(const T& obj)const
{
namespace python = boost :: python;
python :: object result =
obj //如果boost :: optional有一个值,那么
? python :: object(* obj)//延迟到Boost.Python转换器。
:python :: object(); //否则,返回Python None。

// python :: object包含一个作为
//的智能指针的句柄。因为它将
//超出范围,显式增加PyObject的引用
// count,因为调用者期望非借用(即拥有)引用。
return python :: incref(result.ptr());
}

/// @brief用于文档。
const PyTypeObject * get_pytype()const {return 0; }
};

} //命名空间详细信息

/// @brief将boost :: optional转换为Python无如果对象是
///等于boost: :没有。否则,延迟注册的
///类型转换器来退回Boost.Python对象。
struct return_optional
{
template< class T> struct apply
{
// to_python_optional ResultConverter只在运行时检查T是否可转换
//。但是,以下MPL分支导致编译时间
//错误,如果T不是一个boost :: optional通过提供一个类型不是一个
// ResultConverter模型。
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(示例)
{
namespace python = boost :: python;
python :: class_< spam>(Spam)
;

python :: def(make_spam,& make_spam,
python :: return_value_policy< return_optional>());
}

互动用法:



< pre class =lang-python prettyprint-override> >>> 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提供的编译器错误消息的一部分:

 
错误:
中没有名为get_pytype的成员 detail :: return_optional_requires_a_optional_return_type< egg *> '


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

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

I'd like to wrap that somehow so that Python either gets a T by value, or None:

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

Normally if it just returned a T, I could use:

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

But since it returns a boost::optional<T>, it could also return boost::none, which I'd like to end up as Python's None.

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

解决方案

The ResultConverter concept is designed to solve this problem. The return_value_policy CallPolicy model uses a ResultConverterGenerator to create a ResultConverter, and the ResultConverter is used to modify the return value of a function being exposed to Python. In this case, a custom type that implements the ResultConverter concept could be used to either return Python None or instantiate an object with the appropriate Python class. While the documentation list all the type requirements, it may be easier to understood with something closer resembling 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 times, when creating a custom ResultConverter model, one can use template meta-programming to minimize the chances of runtime errors in conversions, and even catch errors at compile time and provide meaningful messages. Here is a complete example of return_optional, a ResultConverter model that takes a C++ boost::optional<T> object, and converts it to the appropriate 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 (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>());
}

Interactive usage:

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

The compile time type checking occurs when the return_optional ResultConvert is attempted to be used with a function that returns a value that is not a boost::optional. For instance, 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 select select the return_optional_requires_a_optional_return_type implementation, and fail compilation. Here is part of the compiler error message clang provides:

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

这篇关于如何包装一个C ++函数返回boost :: optional&lt; T&gt;?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆