概述
在使用 Boost.Python 转换 C++ 程序到 Python 接口的过程中,我们经常需要处理 C++ 参数和返回类型,但是不同编程语言,支持的数据类型会很不一样。虽然对于 int/string 等简单的数据类型,C++ 和 Python 之间不需要任何处理就可以直接使用,但是如 C++ 中的引用、类、指针和 STL 等在 Python 中没有的数据类型,就需要 Boost.Python 进行额外处理才能在 python 程序中使用。
比如,封装以下 c++ 函数:
struct Y
{
X x; Z* z;
int z_value() { return z->value(); }
};
X& f(Y& y, Z* z)
{
y.z = z;
return y.x;
}
在 Python 程序中不能直接调用 X的方法,虽然可以将结果拷贝到一个新的 Python 对象中,如:
>>> f(y, z).set(42) # Result disappears
>>> y.x.get() # No crash, but still bad
3.14
但是这样违背了 Boost.Python 希望尽可能还原 C++ 接口的初衷,且如果 C++ 函数更复杂一点,这种方法又将失效。
为了处理这些复杂的情况,Boost.Python 设计了三种处理模型对应不同的目标和情况。
Boost.Python 的策略
Boost.Python 的三种策略如下:
- CallPolicies 策略: 定义 Boost.Python 转换 C++ 对象生成的 Python 对象的行为。
- ResultConverter 策略: 定义如何将 C++ 的返回值转换成 _python。
- ResultConverterGenerator 策略:这是 ResultConverter 模型的生成器
每个模型下定义了多种模型(以 class 的形式实现),用户在使用时并不直接调用上述策略,而是使用策略下的模型。不同的模型指定的功能和目标不同,常用模型如下:
- with_custodian_and_ward: Ties lifetimes of the arguments
- with_custodian_and_ward_postcall: Ties lifetimes of the arguments and results
- return_internal_reference: Ties lifetime of one argument to that of result
- return_value_policy with T one of:
- reference_existing_object: naive (dangerous) approach
- copy_const_reference: Boost.Python v1 approach
- copy_non_const_reference:
- manage_new_object: Adopt a pointer and hold the instance
注:在 Boost.Python 官方文档中的描述有一个小的混淆,即模型和策略没有分得很清楚,有的地方用策略,有的地方用模型。这里为了明确这一点,将三个大类定义为策略,而其具体实现称为模型。
如何使用 Boost.Python 模型
以上面的 f() 函数为例,我们在定义成 Python 接口时可以:
def("f", f,
return_internal_reference<1,
with_custodian_and_ward<1, 2> >());
- return_internal_reference<1 :返回第一个参数 Y& y 拥有的内部引用 X&。
- with_custodian_and_ward<1, 2> : 通知 Boost.Python ward 指示的参数的生命周期(即第二个参数:Z* z)取决于 custodian 指示的参数的生命周期(即第一个参数:Y& y)。
上述示例中我们定义了两个策略,如果需要定义多个策略,可以采用嵌套的方式:
policy1<args...,
policy2<args...,
policy3<args...> > >
CallPolicies 策略
CallPolicies 策略指定了 Boost.Python 将 C++ 对象转换成 Python 对象后,后者的行为。也就是说这个策略及其下定义的模型主要用于封装 C++ 类。
在模型中定义该 Python 对象的四种行为:
- precall - Python 对象被调用前进行进行 Python 参数元组管理
- result_converter - 如何处理 C++ 返回值
- postcall - 在调用 Python 对象后进行的参数元组和结果管理
- extract_return_type - 用于从给定类型序列中提取返回类型的元函数
即所有 CallPolicies 策略下的模型都要实现下表中的功能:
Expression | Type | Result/Semantics |
---|---|---|
x.precall(a) | convertible to bool | returns false and PyErr_Occurred() != 0 upon failure, true otherwise. |
P::result_converter | A model of ResultConverterGenerator. | An MPL unary Metafunction Class used produce the “preliminary” result object. |
x.postcall(a, r) | convertible to PyObject* | 0 and PyErr_Occurred() != 0 upon failure. Must “conserve references” even in the event of an exception. In other words, if r is not returned, its reference count must be decremented; if another existing object is returned, its reference count must be incremented. |
P::extract_return_type | A model of Metafunction. | An MPL unary Metafunction used extract the return type from a given signature. By default it is derived from mpl::front. |
为了能在同一个可调用对象中使用多个 CallPolicies 模型,Boost.Python 的 CallPolicies 类模板提供了一个链接接口,允许递归地组合他们。此接口采用可选模板参数 Base 的形式,默认为 default_call_policies
。
通常,Base 的 precall 函数在外部模板的 precall 函数之后调用, postcall 函数在外部模板的 postcall 函数之前调用, 如果外部模板提供了 result_converter 函数,它将替换 Base 提供的任何 result_converter函数。
在 Boost.Python 中 包含多个 CallPolicies 策略:default_call_policies, return_arg.hpp, return_internal_reference
和 with_custodian_and_ward
。
default_call_policies 模型
default_call_policies
是 CallPolicies 的一个模型,该模型没有调用前(precall)或调用后(postcall)的行为,并且有一个处理按值返回的 result_converter。默认情况下封装的 C++ 函数和成员函数会使用该模型,如果有需要,用户可以指定其他的模型或派生此类。
部分实现代码如下:
// boost/python/default_call_policies.hpp
struct default_call_policies
{
// Ownership of this argument tuple will ultimately be adopted by
// the caller.
template <class ArgumentPackage>
static bool precall(ArgumentPackage const&)
{
return true;
}
// Pass the result through
template <class ArgumentPackage>
static PyObject* postcall(ArgumentPackage const&, PyObject* result)
{
return result;
}
typedef default_result_converter result_converter;
typedef PyObject* argument_package;
template <class Sig>
struct extract_return_type : mpl::front<Sig>
{
};
};
struct default_result_converter
{
template <class R>
struct apply
{
typedef typename mpl::if_<
mpl::or_<detail::is_pointer<R>, detail::is_reference<R> >
, detail::specify_a_return_value_policy_to_wrap_functions_returning<R>
, boost::python::to_python_value<
typename detail::value_arg<R>::type
>
>::type type;
};
};
// Exceptions for c strings an PyObject*s
template <>
struct default_result_converter::apply<char const*>
{
typedef boost::python::to_python_value<char const*const&> type;
};
template <>
struct default_result_converter::apply<PyObject*>
{
typedef boost::python::to_python_value<PyObject*const&> type;
};
可以看到,default_result_converter
类下的 precall 和 postcall 函数均为空,即不对这两个行为做处理。default_result_converter
类是 ResultConverterGenerator 的模型,可用于包装 C++ 函数,按值返回非指针类型、char const* 和 PyObject*。
这个模型也是其他模型的基类。
使用示例
在 Boost 的源代码中提供了一个默认的 CallPolicies 的结构体,即前文所述。默认情况下,在 Boost 内部也是使用该结构体,但是如果用户想要有其他不同的功能实现,也可以参数上面的示例,构建自己的 CallPolicies。使用方式与普通结构体一样,可以作为元素实例包在其他结构体中(这类结构体在Boost中有大量应用)如:
template <class Class, class CallPolicies = boost::python::default_call_policies>
struct def_init
{
def_init(Class& cl, CallPolicies call_policies = CallPolicies())
: cl(cl)
, call_policies(call_policies)
{}
...
Class& cl;
CallPolicies call_policies;
};
也可以在转换成python接口时直接指定,如:
// default.cpp
BOOST_PYTHON_MODULE(defaults_ext)
{
...
class_<X>("X",no_init)
.def(init<optional<int, char, std::string, double> >("doc of init", args("self", "a", "b", "c", "d")))
.def(init<std::string, bool>(args("self", "s", "b"))[default_call_policies()]) // what's a good policy here?
...
;
}
return_arg 和 return_self 模型
return_arg
及其衍生类 return_self
的实例是基于 default_call_policies
的模型,使用这个模型的封装函数会返回指定的参数(return_arg 是指定参数,而 return_self 通常是 *this)。
// boost/python/return_arg.hpp
// class return_arg
namespace boost { namespace python
{
template <size_t arg_pos=1, class Base = default_call_policies>
struct return_arg : Base
{
static PyObject* postcall(PyObject*, PyObject* result);
struct result_converter{ template <class T> struct apply; };
template <class Sig> struct extract_return_type : mpl::at_c<Sig, arg_pos>{};
};
}}
// class return_self
namespace boost { namespace python
{
template <class Base = default_call_policies>
struct return_self
: return_arg<1,Base>
{};
}}
其中 arg_pos=1 表示需要返回的参数位置。
使用示例
C++ code:
#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/return_arg.hpp>
struct Widget
{
Widget() :sensitive_(true){}
bool get_sensitive() const { return sensitive_; }
void set_sensitive(bool s) { this->sensitive_ = s; }
private:
bool sensitive_;
};
struct Label : Widget
{
Label() {}
std::string get_label() const { return label_; }
void set_label(const std::string &l){ label_ = l; }
private:
std::string label_;
};
using namespace boost::python;
BOOST_PYTHON_MODULE(return_self_ext)
{
class_<widget>("Widget")
.def("sensitive", &Widget::get_sensitive)
.def("sensitive", &Widget::set_sensitive, return_self<>())
;
class_<Label, bases<Widget> >("Label")
.def("label", &Label::get_label)
.def("label", &Label::set_label, return_self<>())
;
}
Python code:
>>> from return_self_ext import *
>>> l1 = Label().label("foo").sensitive(false)
>>> l2 = Label().sensitive(false).label("foo")
使用 return_self 模型之后,虽然 C++ 中,set_label() 函数并没有返回值,但是 Python 中 label(“foo”) 返回了一个可调用的 Python 对象(Label 对象本身)。
return_internal_reference 模型
这个类可以在不拷贝的情况下,直接返回封装函数的指针或引用。围绕指向 C++ 结果对象的指针构建一个 Python 对象,如果 Python 结果是有效的,则应用一些生命周期管理来保持 “self” 对象的活动。NULL 指针返回 None。
其部分实现代码如下:
// boost/python/return_internal_reference.hpp
namespace boost { namespace python
{
template <std::size_t owner_arg = 1, class Base = default_call_policies>
struct return_internal_reference : Base
{
static PyObject* postcall(PyObject*, PyObject* result);
typedef reference_existing_object result_converter;
};
}}
owner_arg=1表示要返回引用或指针对象的参数索引。如果用于封装成员函数,则参数1是目标对象(*this)。需要注意的是,如果目标 Python 对象类型不支持弱引用,则在调用被封装的函数时将引发 Python TypeError 异常。
Base = default_call_policies 表示其父类为 default_call_policies。
使用示例
C++ code:
#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/return_internal_reference.hpp>
class Bar
{
public:
Bar(int x) : x(x) {}
int get_x() const { return x; }
void set_x(int x) { this->x = x; }
private:
int x;
};
class Foo
{
public:
Foo(int x) : b(x) {}
// Returns an internal reference
Bar const& get_bar() const { return b; }
private:
Bar b;
};
using namespace boost::python;
BOOST_PYTHON_MODULE(internal_refs)
{
class_<Bar>("Bar", init<int>())
.def("get_x", &Bar::get_x)
.def("set_x", &Bar::set_x)
;
class_<Foo>("Foo", init<int>())
.def("get_bar", &Foo::get_bar
, return_internal_reference<>())
;
}
Python code:
>>> from internal_refs import *
>>> f = Foo(3)
>>> b1 = f.get_bar()
>>> b2 = f.get_bar()
>>> b1.get_x()
3
>>> b2.get_x()
3
>>> b1.set_x(42)
>>> b2.get_x()
42
C++ 中 Foo() 的返回值是一个类 Bar, 在 Python 程序中, Foo() 返回的对象是根据 Bar 类指针构建的 Python
return_value_policy 模型
return_value_policy
实例化是 CallPolicies 的简单模型,由 ResultConverterGenerator 和可选的 Base CallPolicies 组成。表示产生 Python 返回值的策略。
其部分定义如下:
// boost/python/return_value_policy.hpp
namespace boost { namespace python
{
template <class ResultConverterGenerator, class Base = default_call_policies>
struct return_value_policy : Base
{
typedef ResultConverterGenerator result_converter;
};
}}
Parameter | Requirements | Default |
---|---|---|
ResultConverterGenerator | ResultConverterGenerator 的模型 | |
Base | CallPolicies 模型 | default_call_policies |
使用示例
使用 return_value_policy
模型还需要额外指定所用的 ResultConverterGenerator 模型。具体见 ResultConverterGenerator 模型。
C++ code:
#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/copy_const_reference.hpp>
#include <boost/python/return_value_policy.hpp>
// classes to wrap
struct Bar { int x; }
struct Foo {
Foo(int x) : { b.x = x; }
Bar const& get_bar() const { return b; }
private:
Bar b;
};
// Wrapper code
using namespace boost::python;
BOOST_PYTHON_MODULE(my_module)
{
class_<Bar>("Bar");
class_<Foo>("Foo", init<int>())
.def("get_bar", &Foo::get_bar
, return_value_policy<copy_const_reference>())
;
}
这里的 copy_const_reference 表示封装函数 get_bar() 返回一个 const 修饰的引用。
Python code:
>>> from my_module import *
>>> f = Foo(3) # create a Foo object
>>> b = f.get_bar() # make a copy of the internal Bar object
with_custodian_and_ward 和 with_custodian_and_ward_postcall 模型
这两个类是在函数的两个 Python 参数或结果对象之间建立生命周期依赖关系的工具。只要 Python 对象支持弱引用,则这两个类指定的参数或结果在该对象被销毁之前将一直有效。
为了避免无意中创建空指针,默认情况下在调用底层 C++ 对象之前进行生命周期绑定。但是在调用之前结果对象不可用,因此提供了 with_custodian_and_ward_postcall
来绑定调用后的生命周期。
Note: 这两个类需要目标 Python 对象支持弱引用,否则将触发 Python TypeError 异常。
部分实现代码如下:
// boost/python/with_custodian_and_ward.hpp
namespace boost { namespace python
{
template <std::size_t custodian, std::size_t ward, class Base = default_call_policies>
struct with_custodian_and_ward : Base
{
static bool precall(PyObject* args);
};
struct with_custodian_and_ward_postcall : Base
{
static PyObject* postcall(PyObject* args, PyObject* result);
};
}}
- custodian : 表示开始绑定生命周期的参数索引;1表示第一个参数;如果用于封装成员函数,则参数1表示目标对象(*this)。对于 with_custodian_and_ward_postcall, 0表示结果对象;
- ward : 表示绑定声明周期的最大的参数索引。
- Base : CallPolicies 模型,默认是 default_call_policies
使用示例
with_custodian_and_ward
例如,容器操作 append 通常使用 with_custodian_and_ward<1,2>,这意味着在容器本身处于活动状态时保持参数处于活动状态。
// c++
void reg(Creator* c) { mC = c; }
// python wrapper
.def("reg", &Factory::reg, with_custodian_and_ward<1,2>())
with_custodian_and_ward_postcall
// c++
request_with_value
communicator_irecv_content(const communicator& comm, int source, int tag,
content& c)
{
request_with_value req(comm.irecv(source, tag, c.base()));
req.m_external_value = &c.object;
return req;
}
// python wrapper
.def("irecv", communicator_irecv_content,
(arg("source") = any_source, arg("tag") = any_tag, arg("buffer")),
with_custodian_and_ward_postcall<0, 4>()
);
ResultConverter 策略
ResultConverter 策略指定如何将 C++ 的返回值转换成 _python 返回值。
从文档上看,所有 ResultConverter 下的模型都要实现下表中的功能:
Expression | Type | Semantics |
---|---|---|
C c | 构造实例c | |
c.convertible() | convertible to bool | 如果无法从任何 R 值转换为 Python 对象,则为 false。 |
c (r) | convertible to PyObject* | 指向对应于 r 的 Python 对象的指针。如果 r 无法转换为 _python,此时出发 PyErr_Occurred。 |
c.get_pytype() | PyTypeObject const * | 指向对应于转换结果的 Python Type 对象的指针。如果用于生成文档,则该值为0. |
注:上表来自 Boost.Python 的官方文档,但是我在源代码中没有找到上述 convertible() 函数的实现。
to_python_indirect 模型
<boost/python/to_python_indirect.hpp> 提供了一种方法来构造新的 Python 对象,这些对象通过指针或智能指针保存包装的 C++ 类实例。
模板类 to_python_indirect
使用第二个参数提供的所有权策略将第一个参数类型的对象转换为 python 对象。
部分实现代码如下:
namespace boost { namespace python {
template <class T, class MakeHolder>
struct to_python_indirect
{
template <class U>
inline PyObject* operator()(U const& ref) const;
inline PyTypeObject const* get_pytype()const;
private:
template <class U>
inline PyObject* execute(U* ptr, detail::true_) const;
template <class U>
inline PyObject* execute(U const& x, detail::false_) const;
};
}}
- T 表示要转换成 Python 类型的 C++ 类。
- operator() 函数会创建一个适当类型的 Boost.Python 扩展类实例,使用 MakeHolder 从 x 创建一个 instance_holder,将 instance_holder 安装在新的扩展类实例中,并返回一个指向它的指针。
注:这部分内容从源代码中拷贝出来,与文档有出入。
使用示例
这个示例复制了 reference_existing_object 的功能,但没有一些编译时错误检查。
struct make_reference_holder
{
typedef boost::python::objects::instance_holder* result_type;
template <class T>
static result_type execute(T* p)
{
return new boost::python::objects::pointer_holder<T*, T>(p);
}
};
struct reference_existing_object
{
// metafunction returning the ResultConverter
template <class T>
struct apply
{
typedef boost::python::to_python_indirect<T,make_reference_holder> type;
};
};
to_python_value 模型
to_python_value 是 ResultConverter 的一个模型,它将 C++ 参数复制到一个新的 Python 对象中。
template <class T>
struct to_python_value
: mpl::if_<
detail::value_is_shared_ptr<T>
, detail::shared_ptr_to_python_value<T>
, typename mpl::if_<
mpl::or_<
converter::is_object_manager<T>
, converter::is_reference_to_object_manager<T>
>
, detail::object_manager_to_python_value<T>
, detail::registry_to_python_value<T>
>::type
>::type
{
};
同样地,这里的代码跟文档也有很大出入。
注:其使用方式更多的是作为其他类的模板类,在实际开发 Boost.Python 转换程序时还没看到有什么具体应用使用这个策略。
ResultConverterGenerator 策略
ResultConverterGenerator 是一个 MPL 一元元函数类,对给定 C++ 函数的返回类型 T,该类返回该类型 T 的 ResultConverter。 Boost.Python 中的 ResultConverters 通常检查库的转换器注册表以找到合适的转换器,但不使用注册表的转换器也是可以的。
在下表中,G 表示 ResultConverterGenerator 类型,R 表示可能的 C++ 函数返回类型。
Expression | Requirements |
---|---|
G::apply::type | A ResultConverter type for R. |
如前文所说, ResultConverterGenerator 下的模型需要与 CallPolicies 的 return_value_policy 结合使用。即 return_value_policy 中的 T,
表示产生 Python 返回值的策略。
该策略包含的模型有:
- reference_existing_object : 返回一个已存在实例的Python对象
- copy_non_const_reference :返回一个没有用 const 修饰的对象,C++ 对象会复制到 Python 对象中
- copy_const_reference : 返回一个用 const 修饰的对象,C++ 对象会复制到 Python 对象中
- manage_new_object : 返回一个从堆中新分配的对象,需要在 Python 程序中释放该对象内存空间
- return_by_value : 封装函数的返回值是简单的数据类型的引用,如 int, string 等, 这些值会被复制到 Python 中。
- return_opaqe_pointer : 用于封装返回值是未定义类型的指针的 C++ 函数,会将该值复制到新的 Python 对象中
reference_existing_object 模型
这个模型用于修饰返回值是 C++ 对象引用或指针的函数,转换后的 Python 函数返回值是指向该 C++ 对象的指针。调用封装函数时,不会复制其返回值所引用的值。而是创建一个新的 Python 对象,该对象包含一个 unowned U* 指针,该指针指向封装函数返回值所指向的对象。
这个策略在 return_internal_reference 有应用。 NULL 指针也返回 None。
使用示例
#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/reference_existing_object.hpp>
#include <boost/python/return_value_policy.hpp>
#include <utility>
// classes to wrap
struct Singleton
{
Singleton() : x(0) {}
int exchange(int n) // set x and return the old value
{
std::swap(n, x);
return n;
}
int x;
};
Singleton& get_it()
{
static Singleton just_one;
return just_one;
}
// Wrapper code
using namespace boost::python;
BOOST_PYTHON_MODULE(singleton)
{
def("get_it", get_it,
return_value_policy<reference_existing_object>());
class_<Singleton>("Singleton")
.def("exchange", &Singleton::exchange)
;
}
Python 中 get_it() 函数会返回 Singleton 实例的指针对象。
>>> import singleton
>>> s1 = singleton.get_it()
>>> s2 = singleton.get_it()
>>> id(s1) == id(s2) # s1 and s2 are not the same object
0
>>> s1.exchange(42) # but they reference the same C++ Singleton
0
>>> s2.exchange(99)
42
copy_non_const_reference 模型
用于封装返回非 const 引用的 C++ 函数,以便将引用的值复制到新的 Python 对象中。
使用示例
#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/copy_non_const_reference.hpp>
#include <boost/python/return_value_policy.hpp>
// classes to wrap
struct Bar { int x; }
struct Foo {
Foo(int x) : { b.x = x; }
Bar& get_bar() { return b; }
private:
Bar b;
};
// Wrapper code
using namespace boost::python;
BOOST_PYTHON_MODULE(my_module)
{
class_<Bar>("Bar");
class_<Foo>("Foo", init<int>())
.def("get_bar", &Foo::get_bar
, return_value_policy<copy_non_const_reference>())
;
}
>>> from my_module import *
>>> f = Foo(3) # create a Foo object
>>> b = f.get_bar() # make a copy of the internal Bar object
copy_const_reference 模型
用于封装返回一个引用到常量类型的 C++ 函数,以便将引用的值复制到新的 Python 对象中。
使用示例
#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/copy_const_reference.hpp>
#include <boost/python/return_value_policy.hpp>
// classes to wrap
struct Bar { int x; }
struct Foo {
Foo(int x) : { b.x = x; }
Bar const& get_bar() const { return b; }
private:
Bar b;
};
// Wrapper code
using namespace boost::python;
BOOST_PYTHON_MODULE(my_module)
{
class_<Bar>("Bar");
class_<Foo>("Foo", init<int>())
.def("get_bar", &Foo::get_bar
, return_value_policy<copy_const_reference>())
;
}
>>> from my_module import *
>>> f = Foo(3) # create a Foo object
>>> b = f.get_bar() # make a copy of the internal Bar object
manage_new_object 模型
这个模型用于返回封装函数的新分配的对象,如 new 语句的对象。需要注意的是,像 c++ 中的操作一样,用户需要手动从堆中删除该对象。
使用示例
#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/manage_new_object.hpp>
#include <boost/python/return_value_policy.hpp>
struct Foo {
Foo(int x) : x(x){}
int get_x() { return x; }
int x;
};
Foo* make_foo(int x) { return new Foo(x); }
// Wrapper code
using namespace boost::python;
BOOST_PYTHON_MODULE(my_module)
{
def("make_foo", make_foo, return_value_policy<manage_new_object>())
class_<Foo>("Foo")
.def("get_x", &Foo::get_x)
;
}
>>> from my_module import *
>>> f = make_foo(3) # create a Foo object
>>> f.get_x()
3
return_by_value 模型
这个模型用于封装函数的返回值是任何类型的引用或值。返回值被复制到一个新的 Python 对象中。这里的引用或值是指普通的数据类型,如 string, int 等 c++ 和 python 共有的基本数据类型。
使用示例
#include <boost/python/module.hpp>
#include <boost/python/class.hpp>
#include <boost/python/return_by_value.hpp>
#include <boost/python/return_value_policy.hpp>
// classes to wrap
struct Bar { };
Bar global_bar;
// functions to wrap:
Bar b1();
Bar& b2();
Bar const& b3();
// Wrapper code
using namespace boost::python;
template <class R>
void def_void_function(char const* name, R (*f)())
{
def(name, f, return_value_policy<return_by_value>());
}
BOOST_PYTHON_MODULE(my_module)
{
class_<Bar>("Bar");
def_void_function("b1", b1);
def_void_function("b2", b2);
def_void_function("b3", b3);
}
>>> from my_module import *
>>> b = b1() # each of these calls
>>> b = b2() # creates a brand
>>> b = b3() # new Bar object
return_opaqe_pointer 模型
此模型用于封装返回值是未定义类型的指针的 C++ 函数,会将该值复制到新的 Python 对象中。
除了指定 return_opaque_pointer
策略之外,还必须使用 BOOST_PYTHON_OPAQUE_SPECIALIZED_TYPE_ID 宏来为返回的指针指向的类型定义 type_id 函数的特化。
使用示例
# include <boost/python/return_opaque_pointer.hpp>
# include <boost/python/def.hpp>
# include <boost/python/module.hpp>
# include <boost/python/return_value_policy.hpp>
typedef struct opaque_ *opaque;
opaque the_op = ((opaque) 0x47110815);
opaque get () { return the_op; }
void use (opaque op) {
if (op != the_op)
throw std::runtime_error (std::string ("failed"));
}
void failuse (opaque op) {
if (op == the_op)
throw std::runtime_error (std::string ("success"));
}
BOOST_PYTHON_OPAQUE_SPECIALIZED_TYPE_ID(opaque_)
namespace bpl = boost::python;
BOOST_PYTHON_MODULE(opaque_ext)
{
bpl::def (
"get", &::get, bpl::return_value_policy<bpl::return_opaque_pointer>());
bpl::def ("use", &::use);
bpl::def ("failuse", &::failuse);
}
"""
>>> from opaque_ext import *
>>> #
>>> # Check for correct conversion
>>> use(get())
>>> failuse(get())
Traceback (most recent call last):
...
RuntimeError: success
>>> #
>>> # Check that there is no conversion from integers ...
>>> use(0)
Traceback (most recent call last):
...
TypeError: bad argument type for built-in operation
>>> #
>>> # ... and from strings to opaque objects
>>> use("")
Traceback (most recent call last):
...
TypeError: bad argument type for built-in operation
"""
def run(args = None):
import sys
import doctest
if args is not None:
sys.argv = args
return doctest.testmod(sys.modules.get(__name__))
if __name__ == '__main__':
print "running..."
import sys
sys.exit(run()[0])
参考资料
boost.python/CallPolicy
Boost.Python - Chapter 1. Concepts
Boost Call Policies
Models of CallPolicies
ResultConverter
Models of ResultConverterGenerator
Models of ResultConverter