如何包装返回 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通过值或None
:获得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的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提供的编译器错误消息的一部分:
错误:中没有名为"get_pytype"的成员'详细信息::return_optional_requires_a_optional_return_type<鸡蛋*> '
- "error: no matching function for call to"构造函数错误
- 什么时候调用组成单元对象的析构函数
- 继承函数的重载解析
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- C++模板来检查友元函数的存在
- 递归函数计算序列中的平方和(并输出过程)
- 对RValue对象调用的LValue ref限定成员函数
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 为什么使用 "this" 指针调用派生成员函数?
- 将对象数组的引用传递给函数
- 函数调用中参数的顺序重要吗
- 函数向量_指针有不同的原型,我可以构建一个吗
- 使用不带参数的函数访问结构元素
- 代码在main()中运行,但在函数中出现错误
- 内置函数可查看CPP中的成员变量
- 如何获取std::result_of函数的返回类型
- 如何在c++中为模板函数实例创建快捷方式
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗