Boost C++ 库

……世界上备受推崇且设计精湛的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ Coding Standards

Boost Parameter Library Python 绑定文档 - Boost C++ 函数库

Boost Parameter Library Python 绑定文档

作者 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)

concept ParameterSpec

A ParameterSpec 是一个函数类型K(T),它描述了关键字标签,K和参数类型,T对于参数。

K是以下之一

  • 形式的必需关键字标签
  • ,形式的可选关键字Tag*
  • ,形式的special 关键字Tag**

其中标签是一个关键字标签类型,在 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].

special 关键字

有时希望参数具有一个不同于参数类型的默认值。此技术对于基于参数是否存在进行简单的标签分派很有用。例如

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_()]);
}

在上面的例子中,默认值的类型是colormpl::false_,一个与用户可能提供的任何颜色映射都不同的类型。

绑定上述情况时,默认类型为color将不可转换为参数类型。因此,我们需要标记color关键字作为special 关键字。这通过将标签指定为tag::color**在绑定函数时(有关标签的更多详细信息,请参阅 concept ParameterSpec)。通过这样做,我们告诉绑定函数它需要生成两个重载,一个带有color参数存在,一个不存在。如果有两个special 关键字,则需要生成四个重载。生成的重载数量等于 2N,其中Nspecial 关键字的数量。


类模板init

定义了一个启用了命名参数的构造函数。

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;
};

initrequirements

  • ParameterSpecs是一个 MPL sequence,其中每个元素都是 ParameterSpec 的模型。

  • 对于每一个Nin[U,V],其中[U,V]arity rangeParameterSpecs, 类别必须支持这些表达式

    表达式 返回类型 要求
    Class(a0, …, aN) - a0aN是带标签的参数。

template <class CallPolicies> operator[](CallPolicies const&)

返回一个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;
};

调用requirements

  • ParameterSpecs是一个 MPL sequence,其中除第一个元素外的每个元素都是 ParameterSpec 的模型。第一个元素是结果类型c(…).

  • 类别必须支持这些表达式,其中c是的一个实例类别:

    表达式 返回类型 要求
    c(a0, …, aN) 可转换为R a0aN是带标签的参数。

    对于每一个Nin[U,V],其中[U,V]arity rangeParameterSpecs.

template <class CallPolicies> operator[](CallPolicies const&)

返回一个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);
};

函数requirements

  • ParameterSpecs是一个 MPL sequence,其中除第一个元素外的每个元素都是 ParameterSpec 的模型。第一个元素是结果类型c.f(…),其中f是成员函数。

  • 的一个实例Fwd必须支持此表达式

    表达式 返回类型 要求
    fwd(boost::type<R>(), self, a0, …, aN) 可转换为R self是对应该调用函数的对象的引用。a0aN是带标签的参数。

    对于每一个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)>
            >()
        );
}

函数模板def

在当前 Python 作用域中定义了一个启用了命名参数的自由函数。

template <class Fwd, class ParameterSpecs>
void def(char const* name);

defrequirements

  • ParameterSpecs是一个 MPL sequence,其中除第一个元素外的每个元素都是 ParameterSpec 的模型。第一个元素是结果类型f(…),其中f是函数。

  • 的一个实例Fwd必须支持此表达式

    表达式 返回类型 要求
    fwd(boost::type<R>(), a0, …, aN) 可转换为R a0aN是带标签的参数。

    对于每一个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 绑定库需要部分模板特化