作者 | David Abrahams Daniel Wallin |
---|---|
联系方式 | dave@boost-consulting.com, daniel@boostpro.com |
组织 | BoostPro Computing |
日期 | $Date$ |
版权 | Copyright David Abrahams, Daniel Wallin 2005-2009。根据 Boost 软件许可证 1.0 版分发。(请参阅随附的 LICENSE_1_0.txt 文件或访问 https://boost.ac.cn/LICENSE_1_0.txt 获取副本) |
摘要
使能够将启用了 Boost.Parameter 的函数、运算符和构造函数绑定到 Python。
boost/parameter/python.hpp引入了一组 def_visitors,可以使用它们通过 Boost.Python 轻松地将启用了 Boost.Parameter 的成员函数暴露给 Python。它还提供了一个函数模板def(),可用于公开启用了 Boost.Parameter 的自由函数。
绑定启用了 Boost.Parameter 的函数时,必须指定关键字标签。此外,由于启用了 Boost.Parameter 的函数是模板,因此必须指定所需的函数签名。
关键字标签和关联的参数类型被指定为 MPL Sequence,使用下面 ParameterSpec 中描述的函数类型语法。此外,boost::parameter::python::functionandboost::parameter::python::def需要一个具有转发重载的类。我们将在下面的教程部分更详细地研究这一点。
在本节中,我们将概述将简单的启用了 Boost.Parameter 的成员函数绑定到 Python 所需的步骤。要理解本节,需要了解 Boost.Parameter 宏。
我们感兴趣的类和成员函数如下所示
#include <boost/parameter/keyword.hpp> #include <boost/parameter/preprocessor.hpp> #include <boost/parameter/python.hpp> #include <boost/python.hpp> // First the keywords BOOST_PARAMETER_KEYWORD(tag, title) BOOST_PARAMETER_KEYWORD(tag, width) BOOST_PARAMETER_KEYWORD(tag, height) class window { public: BOOST_PARAMETER_MEMBER_FUNCTION( (void), open, tag, (required (title, (std::string))) (optional (width, (unsigned), 400) (height, (unsigned), 400)) ) { … function implementation … } };
它定义了一组名为的重载成员函数open带有一个必需参数和两个可选参数。要将此成员函数绑定到 Python,我们使用绑定实用程序boost::parameter::python::function. boost::parameter::python::function是一个 def_visitor,我们将对其进行实例化并传递给boost::python::class_::def().
要使用boost::parameter::python::function我们首先需要定义一个具有转发重载的类。这是必需的,因为window::open()是一个函数模板,因此我们无法以任何其他方式引用它。
struct open_fwd { template <class A0, class A1, class A2> void operator()( boost::type<void>, window& self , A0 const& a0, A1 const& a1, A2 const& a2 ) { self.open(a0, a1, a2); } };
第一个参数,boost::type<void>,告诉转发重载返回类型应为何。在这种情况下,我们知道它始终是 void,但在某些情况下,当导出启用了 Boost.Parameter 的模板的多个特化时,我们需要使用该参数来推导返回类型。
window::open()总共需要 3 个参数,因此转发函数也需要 3 个参数。
注意
尽管有两个可选参数,但我们只需要一个转发类中的重载。在某些特殊情况下需要多个重载;请参阅 special keywords。
接下来,我们将定义模块并导出类
BOOST_PYTHON_MODULE(my_module) { using namespace boost::python; namespace py = boost::parameter::python; namespace mpl = boost::mpl; class_<window>("window") .def( "open", py::function< open_fwd , mpl::vector< void , tag::title(std::string) , tag::width*(unsigned) , tag::height*(unsigned) > >() ); }
py::function接受两个参数。第一个是我们之前定义的具有转发重载的类。第二个是一个 MPL Sequence,其中包含函数关键字标签类型和参数类型,这些都指定为函数类型。指针语法在tag::width*andtag::height*表示该参数是可选的。的第一个元素 MPL Sequence 是函数的返回类型,在本例中为void,作为第一个参数传递给operator()在转发类中。
就是这样!现在可以在 Python 中使用此类,语法符合预期
>>> w = my_module.window() >>> w.open(title = "foo", height = 20)
A ParameterSpec 是一个函数类型K(T),它描述了关键字标签,K和参数类型,T对于参数。
K是以下之一
其中标签是一个关键字标签类型,在 boost::parameter::keyword 的特化中使用。
的arity range MPL Sequence ParameterSpec's 定义为闭区间
[ mpl::size<S> - number of special keyword tags in S, mpl::size<S> ]
例如,的arity rangempl::vector2<x(int),y(int)>是[2,2],的arity rangempl::vector2<x(int),y*(int)>是[2,2]和的arity rangempl::vector2<x(int),y**(int)>是[1,2].
有时希望参数具有一个不同于参数类型的默认值。此技术对于基于参数是否存在进行简单的标签分派很有用。例如
namespace core { template <class ArgumentPack> void dfs_dispatch(ArgumentPack const& args, mpl::false_) { …compute and use default color map… } template <class ArgumentPack, class ColorMap> void dfs_dispatch(ArgumentPack const& args, ColorMap colormap) { …use colormap… } } template <class ArgumentPack> void depth_first_search(ArgumentPack const& args) { core::dfs_dispatch(args, args[color | mpl::false_()]); }
在上面的例子中,默认值的类型是color是mpl::false_,一个与用户可能提供的任何颜色映射都不同的类型。
绑定上述情况时,默认类型为color将不可转换为参数类型。因此,我们需要标记color关键字作为special 关键字。这通过将标签指定为tag::color**在绑定函数时(有关标签的更多详细信息,请参阅 concept ParameterSpec)。通过这样做,我们告诉绑定函数它需要生成两个重载,一个带有color参数存在,一个不存在。如果有两个special 关键字,则需要生成四个重载。生成的重载数量等于 2N,其中N是special 关键字的数量。
定义了一个启用了命名参数的构造函数。
template <class ParameterSpecs> struct init : python::def_visitor<init<ParameterSpecs> > { template <class Class> void def(Class& class_); template <class CallPolicies> def_visitor operator[](CallPolicies const& policies) const; };
ParameterSpecs是一个 MPL sequence,其中每个元素都是 ParameterSpec 的模型。
对于每一个Nin[U,V],其中[U,V]是arity rangeParameterSpecs, 类别必须支持这些表达式
表达式 | 返回类型 | 要求 |
---|---|---|
Class(a0, …, aN) | - | a0…aN是带标签的参数。 |
返回一个def_visitor等同于*this,但它在创建绑定时使用 CallPolicies。
#include <boost/parameter/keyword.hpp> #include <boost/parameter/preprocessor.hpp> #include <boost/parameter/python.hpp> #include <boost/python.hpp> #include <boost/mpl/vector.hpp> BOOST_PARAMETER_KEYWORD(tag, x) BOOST_PARAMETER_KEYWORD(tag, y) struct base { template <class ArgumentPack> base(ArgumentPack const& args) { … use args … } }; class X : base { public: BOOST_PARAMETER_CONSTRUCTOR(X, (base), tag, (required (x, *)) (optional (y, *)) ) }; BOOST_PYTHON_MODULE(module name) { using namespace boost::python; namespace py = boost::parameter::python; namespace mpl = boost::mpl; class_<X>("X", no_init) .def( py::init< mpl::vector<tag::x(int), tag::y*(int)> >() ); }
定义了一个__call__运算符,映射到operator()在 C++ 中。
template <class ParameterSpecs> struct call : python::def_visitor<call<ParameterSpecs> > { template <class Class> void def(Class& class_); template <class CallPolicies> def_visitor operator[](CallPolicies const& policies) const; };
ParameterSpecs是一个 MPL sequence,其中除第一个元素外的每个元素都是 ParameterSpec 的模型。第一个元素是结果类型c(…).
类别必须支持这些表达式,其中c是的一个实例类别:
表达式 | 返回类型 | 要求 |
---|---|---|
c(a0, …, aN) | 可转换为R | a0…aN是带标签的参数。 |
对于每一个Nin[U,V],其中[U,V]是arity rangeParameterSpecs.
返回一个def_visitor等同于*this,但它在创建绑定时使用 CallPolicies。
#include <boost/parameter/keyword.hpp> #include <boost/parameter/preprocessor.hpp> #include <boost/parameter/python.hpp> #include <boost/python.hpp> #include <boost/mpl/vector.hpp> BOOST_PARAMETER_KEYWORD(tag, x) BOOST_PARAMETER_KEYWORD(tag, y) namespace parameter = boost::parameter; typedef parameter::parameters< parameter::required<tag::x> , parameter::optional<tag::y> > call_parameters; class X { public: template <class ArgumentPack> int call_impl(ArgumentPack const& args) { … use args … } template <class A0> int operator()(A0 const& a0) { return call_impl(call_parameters()(a0)); } template <class A0, class A1> int operator()(A0 const& a0, A1 const& a1) { return call_impl(call_parameters()(a0,a1)); } }; BOOST_PYTHON_MODULE(module name) { using namespace boost::python; namespace py = parameter::python; namespace mpl = boost::mpl; class_<X>("X") .def( py::call< mpl::vector<int, tag::x(int), tag::y*(int)> >() ); }
定义了一个启用了命名参数的成员函数。
template <class Fwd, class ParameterSpecs> struct function : python::def_visitor<function<Fwd, ParameterSpecs> > { template <class Class, class Options> void def(Class& class_, char const* name, Options const& options); };
ParameterSpecs是一个 MPL sequence,其中除第一个元素外的每个元素都是 ParameterSpec 的模型。第一个元素是结果类型c.f(…),其中f是成员函数。
的一个实例Fwd必须支持此表达式
表达式 | 返回类型 | 要求 |
---|---|---|
fwd(boost::type<R>(), self, a0, …, aN) | 可转换为R | self是对应该调用函数的对象的引用。a0…aN是带标签的参数。 |
对于每一个Nin[U,V],其中[U,V]是arity rangeParameterSpecs.
此示例将一个成员函数导出到 Pythonf(int x, int y = …)。的 ParameterSpec 序列mpl::vector2<tag::x(int), tag::y*(int)>的arity range 是 [2,2],因此我们只需要一个转发重载。
#include <boost/parameter/keyword.hpp> #include <boost/parameter/preprocessor.hpp> #include <boost/parameter/python.hpp> #include <boost/python.hpp> #include <boost/mpl/vector.hpp> BOOST_PARAMETER_KEYWORD(tag, x) BOOST_PARAMETER_KEYWORD(tag, y) class X { public: BOOST_PARAMETER_MEMBER_FUNCTION((void), f, tag, (required (x, *)) (optional (y, *, 1)) ) { … } }; struct f_fwd { template <class A0, class A1> void operator()(boost::type<void>, X& self, A0 const& a0, A1 const& a1) { self.f(a0, a1); } }; BOOST_PYTHON_MODULE(module name) { using namespace boost::python; namespace py = boost::parameter::python; namespace mpl = boost::mpl; class_<X>("X") .def("f", py::function< f_fwd , mpl::vector<void, tag::x(int), tag::y*(int)> >() ); }
在当前 Python 作用域中定义了一个启用了命名参数的自由函数。
template <class Fwd, class ParameterSpecs> void def(char const* name);
ParameterSpecs是一个 MPL sequence,其中除第一个元素外的每个元素都是 ParameterSpec 的模型。第一个元素是结果类型f(…),其中f是函数。
的一个实例Fwd必须支持此表达式
表达式 | 返回类型 | 要求 |
---|---|---|
fwd(boost::type<R>(), a0, …, aN) | 可转换为R | a0…aN是带标签的参数。 |
对于每一个Nin[U,V],其中[U,V]是arity rangeParameterSpecs.
此示例导出函数f(int x, int y = …)。的 ParameterSpec 序列mpl::vector2<tag::x(int), tag::y*(int)>的arity range 是 [2,2],因此我们只需要一个转发重载。
BOOST_PARAMETER_FUNCTION((void), f, tag, (required (x, *)) (optional (y, *, 1)) ) { … } struct f_fwd { template <class A0, class A1> void operator()(boost::type<void>, A0 const& a0, A1 const& a1) { f(a0, a1); } }; BOOST_PYTHON_MODULE(…) { def< f_fwd , mpl::vector< void, tag::x(int), tag::y*(int) > >("f"); }
Boost.Parameter Python 绑定库需要部分模板特化。