Actors revisited

| Actors revisited |

This class is a protocol class for all actors. This class is essentially an interface contract. The actor class does not really know how how to act on anything but instead relies on the template parameter BaseT (from which the actor will derive from) to do the actual action. The template class actor is declared as:

`

        template <typename BaseT>
        struct actor : public BaseT {

            actor();
            actor(BaseT const& base);

            /*...member functions...*/
        };

`

Curiously Recurring Template Pattern Inverse Notice that actor derives from its template argument BaseT. This is the inverse of the curiously recurring template pattern (CRTP). With the CRTP, the actor is an abstract class with a DerivedT template parameter that is assumed to be its parametric subclass. This pattern however, "parametric base class pattern" (PBCP) for lack of a name, inverses the inheritance and makes actor a concrete class. Anyway, be it CRTP or PBCP, actor is a protocol class and either BaseT or DerivedT will have to conform to its protocol. Both CRTP and PBCP techniques has its pros and cons, of which is outside the scope of this document. CRTP should really be renamed "parametric subclass pattern (PSCP), but again, that's another story.

An actor is a functor that is capable of accepting arguments up to a predefined maximum. It is up to the base class to do the actual processing or possibly to limit the arity (no. of arguments) passed in. Upon invocation of the functor through a supplied operator(), the actor funnels the arguments passed in by the client into a tuple and calls the base class' eval member function.

Schematically:

`

        arg0 ---------|
        arg1 ---------|
        arg2 ---------|---> tupled_args ---> base.eval
        ...           |
        argN ---------|

        actor::operator()(arg0, arg1... argN)
          ---> BaseT::eval(tupled_args);

`

Actor base classes from which this class inherits from are expected to have a corresponding member function eval compatible with the conceptual Interface:

`

        template <typename TupleT>
        actor_return_type
        eval(TupleT const& args) const;

`

where args are the actual arguments passed in by the client funneled into a tuple (see tuple for details).

The actor_return_type can be anything. Base classes are free to return any type, even argument dependent types (types that are deduced from the types of the arguments). After evaluating the parameters and doing some computations or actions, the eval member function concludes by returning something back to the client. To do this, the forwarding function (the actor's operator()) needs to know the return type of the eval member function that it is calling. For this purpose, actor base classes are required to provide a nested template class:

`

        template <typename TupleT>
        struct result;

`

This auxiliary class provides the result type information returned by the eval member function of a base actor class. The nested template class result should have a typedef 'type' that reflects the return type of its member function eval. It is basically a type computer that answers the question "given arguments packed into a TupleT type, what will be the result type of the eval member function of ActorT?".

There is a global template class actor_result declared in namespace phoenix scope that queries the actor's result type given a tuple. Here is the class actor_result's declaration:

`

        template <typename ActorT, typename TupleT>
        struct actor_result {

            typedef typename ActorT::template result<TupleT>::type type;
            typedef typename remove_reference<type>::type plain_type;
        };

`

- type is the actual return type
- plain_type is the return type stripped from references.

Given an actor type ActorT and a TupleT, we can get the actor's return type this way:

`

        typedef typename actor_result<ActorT, TupleT>::type
            actor_return_type;

`

where actor_return_type is the actual type returned by ActorT's eval member function given some arguments packed in a TupleT.

For reference, here's a typical actor::operator() that accepts two (2) arguments:

`

        template <typename BaseT>
        template <typename T0, typename T1>
        inline typename actor_result<BaseT, tuple<T0&, T1&> >::type
        actor<BaseT>::operator()(T0& _0, T1& _1) const
        {
            return BaseT::eval(tuple<T0&, T1&>(_0, _1));
        }

`

Forwarding Function Problem _0 and _1 are references. Hence the arguments cannot accept non-const temporaries and literal constants. This is a current C++ language issue known as the "forwarding function problem" that is currently being discussed. The problem is that given an arbitrary function f, using current C++ language rules, one cannot create a forwarding function f' that transparently assumes the arguments of f.

Copyright © 2001-2002 Joel de Guzman
Use, modification and distribution is subject to 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)