| 作者 | 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 绑定库需要部分模板特化。