| Home | Libraries | People | FAQ | More

#### Generating Repetitive Code with the Preprocessor

Sometimes as an EDSL designer, to make the lives of your users easy, you have to make your own life hard. Giving your users natural and flexible syntax often involves writing large numbers of repetitive function overloads. It can be enough to give you repetitive stress injury! Before you hurt yourself, check out the macros Proto provides for automating many repetitive code-generation chores.

Imagine that we are writing a lambda EDSL, and we would like to enable syntax for constructing temporary objects of any type using the following syntax:

    // A lambda expression that takes two arguments and
    // uses them to construct a temporary std::complex<>
    construct< std::complex<int> >( _1, _2 )

For the sake of the discussion, imagine that we already have a function object template `construct_impl<>` that accepts arguments and constructs new objects from them. We would want the above lambda expression to be equivalent to the following:

    // The above lambda expression should be roughly equivalent
    // to the following:
    proto::make_expr<proto::tag::function>(
        construct_impl<std::complex<int> >() // The function to invoke lazily
      , boost::ref(_1)                       // The first argument to the function
      , boost::ref(_2)                       // The second argument to the function
    );

We can define our `construct()` function template as follows:

    template<typename T, typename A0, typename A1>
    typename proto::result_of::make_expr<
        proto::tag::function
      , construct_impl<T>
      , A0 const &
      , A1 const &
    >::type const
    construct(A0 const &a0, A1 const &a1)
    {
        return proto::make_expr<proto::tag::function>(
            construct_impl<T>()
          , boost::ref(a0)
          , boost::ref(a1)
        );
    }

This works for two arguments, but we would like it to work for any number of arguments, up to ( ``BOOST_PROTO_MAX_ARITY`` - 1). (Why "- 1"? Because one child is taken up by the `construct_impl<T>()` terminal leaving room for only ( ``BOOST_PROTO_MAX_ARITY`` - 1) other children.)

For cases like this, Proto provides the ``BOOST_PROTO_REPEAT`()` and ``BOOST_PROTO_REPEAT_FROM_TO`()` macros. To use it, we turn the function definition above into a macro as follows:

    #define M0(N, typename_A, A_const_ref, A_const_ref_a, ref_a)  \
    template<typename T, typename_A(N)>                           \
    typename proto::result_of::make_expr<                         \
        proto::tag::function                                      \
      , construct_impl<T>                                         \
      , A_const_ref(N)                                            \
    >::type const                                                 \
    construct(A_const_ref_a(N))                                   \
    {                                                             \
        return proto::make_expr<proto::tag::function>(            \
            construct_impl<T>()                                   \
          , ref_a(N)                                              \
        );                                                        \
    }

Notice that we turned the function into a macro that takes 5 arguments. The first is the current iteration number. The rest are the names of other macros that generate different sequences. For instance, Proto passes as the second parameter the name of a macro that will expand to `typename A0, typename A1, ...`.

Now that we have turned our function into a macro, we can pass the macro to ``BOOST_PROTO_REPEAT_FROM_TO`()`. Proto will invoke it iteratively, generating all the function overloads for us.

    // Generate overloads of construct() that accept from
    // 1 to BOOST_PROTO_MAX_ARITY-1 arguments:
    BOOST_PROTO_REPEAT_FROM_TO(1, BOOST_PROTO_MAX_ARITY, M0)
    #undef M0

###### Non-Default Sequences

As mentioned above, Proto passes as the last 4 arguments to your macro the names of other macros that generate various sequences. The macros ``BOOST_PROTO_REPEAT`()` and ``BOOST_PROTO_REPEAT_FROM_TO`()` select defaults for these parameters. If the defaults do not meet your needs, you can use ``BOOST_PROTO_REPEAT_EX`()` and ``BOOST_PROTO_REPEAT_FROM_TO_EX`()` and pass different macros that generate different sequences. Proto defines a number of such macros for use as parameters to ``BOOST_PROTO_REPEAT_EX`()` and ``BOOST_PROTO_REPEAT_FROM_TO_EX`()`. Check the reference section for `boost/proto/repeat.hpp` for all the details.

Also, check out ``BOOST_PROTO_LOCAL_ITERATE`()`. It works similarly to ``BOOST_PROTO_REPEAT`()` and friends, but it can be easier to use when you want to change one macro argument and accept defaults for the others.