Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext
Building Custom Primitive Transforms

In previous sections, we've seen how to compose larger transforms out of smaller transforms using function types. The smaller transforms from which larger transforms are composed are primitive transforms, and Proto provides a bunch of common ones such as _child0 and _value. In this section we'll see how to author your own primitive transforms.

[Note] Note

There are a few reasons why you might want to write your own primitive transforms. For instance, your transform may be complicated, and composing it out of primitives becomes unwieldy. You might also need to work around compiler bugs on legacy compilers that make composing transforms using function types problematic. Finally, you might also decide to define your own primitive transforms to improve compile times. Since Proto can simply invoke a primitive transform directly without having to process arguments or differentiate callable transforms from object transforms, primitive transforms are more efficient.

Primitive transforms inherit from proto::transform<> and have a nested impl<> template that inherits from proto::transform_impl<>. For example, this is how Proto defines the _child_c<N> transform, which returns the N-th child of the current expression:

namespace boost { namespace proto
{
    // A primitive transform that returns N-th child
    // of the current expression.
    template<int N>
    struct _child_c : transform<_child_c<N> >
    {
        template<typename Expr, typename State, typename Data>
        struct impl : transform_impl<Expr, State, Data>
        {
            typedef
                typename result_of::child_c<Expr, N>::type
            result_type;

            result_type operator ()(
                typename impl::expr_param expr
              , typename impl::state_param state
              , typename impl::data_param data
            ) const
            {
                return proto::child_c<N>(expr);
            }
        };
    };

    // Note that _child_c<N> is callable, so that
    // it can be used in callable transforms, as:
    //   _child_c<0>(_child_c<1>)
    template<int N>
    struct is_callable<_child_c<N> >
      : mpl::true_
    {};
}}

The proto::transform<> base class provides the operator() overloads and the nested result<> template that make your transform a valid function object. These are implemented in terms of the nested impl<> template you define.

The proto::transform_impl<> base class is a convenience. It provides some nested typedefs that are generally useful. They are specified in the table below:

Table 1.12. proto::transform_impl<Expr, State, Data> typedefs

typedef

Equivalent To

expr

typename remove_reference<Expr>::type

state

typename remove_reference<State>::type

data

typename remove_reference<Data>::type

expr_param

typename add_reference<typename add_const<Expr>::type>::type

state_param

typename add_reference<typename add_const<State>::type>::type

data_param

typename add_reference<typename add_const<Data>::type>::type


You'll notice that _child_c::impl::operator() takes arguments of types expr_param, state_param, and data_param. The typedefs make it easy to accept arguments by reference or const reference accordingly.

The only other interesting bit is the is_callable<> specialization, which will be described in the next section.


PrevUpHomeNext