Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

The Boost Parameter Library Python Binding Documentation

Authors: David Abrahams
Daniel Wallin
Contact: dave@boost-consulting.com, daniel@boostpro.com
Organization: BoostPro Computing
Date: $Date$
Copyright: Copyright David Abrahams, Daniel Wallin 2005-2009. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

Abstract

Makes it possible to bind Boost.Parameter-enabled functions, operators and constructors to Python.

Boost

Contents

Introduction

boost/parameter/python.hpp introduces a group of def_visitors that can be used to easily expose Boost.Parameter-enabled member functions to Python with Boost.Python. It also provides a function template def() that can be used to expose Boost.Parameter-enabled free functions.

When binding a Boost.Parameter enabled function, the keyword tags must be specified. Additionally, because Boost.Parameter enabled functions are templates, the desired function signature must be specified.

The keyword tags and associated argument types are specified as an MPL Sequence, using the function type syntax described in ParameterSpec below. Additionally, boost::parameter::python::function and boost::parameter::python::def requires a class with forwarding overloads. We will take a closer look at how this is done in the tutorial section below.

Tutorial

In this section we will outline the steps needed to bind a simple Boost.Parameter-enabled member function to Python. Knowledge of the Boost.Parameter macros are required to understand this section.

The class and member function we are interested in binding looks like this:

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

It defines a set of overloaded member functions called open with one required parameter and two optional ones. To bind this member function to Python we use the binding utility boost::parameter::python::function. boost::parameter::python::function is a def_visitor that we'll instantiate and pass to boost::python::class_::def().

To use boost::parameter::python::function we first need to define a class with forwarding overloads. This is needed because window::open() is a function template, so we can't refer to it in any other way.

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

The first parameter, boost::type<void>, tells the forwarding overload what the return type should be. In this case we know that it's always void but in some cases, when we are exporting several specializations of a Boost.Parameter-enabled template, we need to use that parameter to deduce the return type.

window::open() takes a total of 3 parameters, so the forwarding function needs to take three parameters as well.

Note

We only need one overload in the forwarding class, despite the fact that there are two optional parameters. There are special circumstances when several overload are needed; see special keywords.

Next we'll define the module and export the class:

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 is passed two parameters. The first one is the class with forwarding overloads that we defined earlier. The second one is an MPL Sequence with the keyword tag types and argument types for the function specified as function types. The pointer syntax used in tag::width* and tag::height* means that the parameter is optional. The first element of the MPL Sequence is the return type of the function, in this case void, which is passed as the first argument to operator() in the forwarding class.

That's it! This class can now be used in Python with the expected syntax:

>>> w = my_module.window()
>>> w.open(title = "foo", height = 20)

concept ParameterSpec

A ParameterSpec is a function type K(T) that describes both the keyword tag, K, and the argument type, T, for a parameter.

K is either:

where Tag is a keyword tag type, as used in a specialization of boost::parameter::keyword.

The arity range for an MPL Sequence of ParameterSpec's is defined as the closed range:

[ mpl::size<S> - number of special keyword tags in S, mpl::size<S> ]

For example, the arity range of mpl::vector2<x(int),y(int)> is [2,2], the arity range of mpl::vector2<x(int),y*(int)> is [2,2] and the arity range of mpl::vector2<x(int),y**(int)> is [1,2].

special keywords

Sometimes it is desirable to have a default value for a parameter that differ in type from the parameter. This technique is useful for doing simple tag-dispatching based on the presence of a parameter. For example:

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

In the above example the type of the default for color is mpl::false_, a type that is distinct from any color map that the user might supply.

When binding the case outlined above, the default type for color will not be convertible to the parameter type. Therefore we need to tag the color keyword as a special keyword. This is done by specifying the tag as tag::color** when binding the function (see concept ParameterSpec for more details on the tagging). By doing this we tell the binding functions that it needs to generate two overloads, one with the color parameter present and one without. Had there been two special keywords, four overloads would need to be generated. The number of generated overloads is equal to 2N, where N is the number of special keywords.


class template init

Defines a named parameter enabled constructor.

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

init requirements

  • ParameterSpecs is an MPL sequence where each element is a model of ParameterSpec.

  • For every N in [U,V], where [U,V] is the arity range of ParameterSpecs, Class must support these expressions:

    Expression Return type Requirements
    Class(a0, …, aN) - a0aN are tagged arguments.

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

Returns a def_visitor equivalent to *this, except that it uses CallPolicies when creating the binding.

Example

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

class template call

Defines a __call__ operator, mapped to operator() in 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;
};

call requirements

  • ParameterSpecs is an MPL sequence where each element except the first models ParameterSpec. The first element is the result type of c(…).

  • Class must support these expressions, where c is an instance of Class:

    Expression Return type Requirements
    c(a0, …, aN) Convertible to R a0aN are tagged arguments.

    For every N in [U,V], where [U,V] is the arity range of ParameterSpecs.

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

Returns a def_visitor equivalent to *this, except that it uses CallPolicies when creating the binding.

Example

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

class template function

Defines a named parameter enabled member function.

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

function requirements

  • ParameterSpecs is an MPL sequence where each element except the first models ParameterSpec. The first element is the result type of c.f(…), where f is the member function.

  • An instance of Fwd must support this expression:

    Expression Return type Requirements
    fwd(boost::type<R>(), self, a0, …, aN) Convertible to R self is a reference to the object on which the function should be invoked. a0aN are tagged arguments.

    For every N in [U,V], where [U,V] is the arity range of ParameterSpecs.

Example

This example exports a member function f(int x, int y = …) to Python. The sequence of ParameterSpec's mpl::vector2<tag::x(int), tag::y*(int)> has an arity range of [2,2], so we only need one forwarding overload.

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

function template def

Defines a named parameter enabled free function in the current Python scope.

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

def requirements

  • ParameterSpecs is an MPL sequence where each element except the first models ParameterSpec. The first element is the result type of f(…), where f is the function.

  • An instance of Fwd must support this expression:

    Expression Return type Requirements
    fwd(boost::type<R>(), a0, …, aN) Convertible to R a0aN are tagged arguments.

    For every N in [U,V], where [U,V] is the arity range of ParameterSpecs.

Example

This example exports a function f(int x, int y = …) to Python. The sequence of ParameterSpec's mpl::vector2<tag::x(int), tag::y*(int)> has an arity range of [2,2], so we only need one forwarding overload.

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

Portability

The Boost.Parameter Python binding library requires partial template specialization.