简而言之,不能使用 Boost.Python 直接公开返回 int* 的函数,因为在 Python 中没有有意义的对应类型,因为整数是不可变的。
- 如果目标是向 Python 返回一个数字,则按值返回
int。这可能需要使用辅助函数来适配旧版 API。
- 如果目标是返回指向使用 new 表达式分配的对象的指针,则该对象必须是类或联合,并且必须使用特定策略公开函数以指示所有权责任。
与其立即提出最终解决方案,我更愿意花时间逐步解决编译器错误。对于 Boost.Python,有时使用 C++11 之前的静态断言在编译器消息中提供指令。不幸的是,在繁重的模板中很难找到它们。
以下代码:
#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);
}
在 clang 中生成以下相关输出,其中重要的细节以粗体突出显示:
.../boost/python/detail/caller.hpp:102:98: 错误:
'boost::python::detail:: 中没有名为 'get_pytype' 的成员
指定_a_return_value_policy_to_wrap_functions_returning'
...create_result_converter((PyObject*)0, (ResultConverter *)0,
(ResultConverter *)0).g...
Boost.Python 通知我们需要为返回 int* 的函数指定 boost::python::return_value_policy。 ResultConverterGenerators有多种型号。通常,这些策略用于控制返回对象的所有权或生命周期语义。由于原始代码中的函数直接返回一个指向 Python 的新指针,因此如果希望调用者负责删除该对象,则 boost::python::manage_new_object 是合适的。
为对象管理指定策略仍然失败:
#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>());
}
产生以下相关输出:
.../boost/python/object/make_instance.hpp:27:9:
错误:没有匹配的函数调用“assertion_failed”
BOOST_MPL_ASSERT((mpl::or_, is_union >));
在这种情况下,Boost.Python 通知我们从具有managed_new_object ResultConverterGenerator 的函数返回的对象必须是class 或union。对于int*,最合适的解决方案是通过Boost.Python层时按值返回int。但是,为了完整起见,以下演示:
- 使用辅助函数来适配旧版 API。
- 如何使用返回指针的工厂函数来限制用户定义类型的创建。
#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>());
}
互动使用:
>>> 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)