Runtime Dynamic Dispatch

PrevUpHomeNext

Invoker

Binding Arguments
Dealing With Exceptions
Accumulators

The invoker is a special parametric factory function which returns a callable instance of the invoker type. The returned instance is dependent on the type of the dispatcher being passed as the parameter. To use the invoker, you just need to call the boost::dispatch::invoke_ method and pass the dispatcher instance to bind it to -- the resulting object can then be activated by calling the function operator, and then passing an index via the shift left operator (operator<<)

boost::dispatch::dispatcher<void ()> void_dispatcher;
boost::dispatch::invoke_(void_dispatcher)() << 0 << 1 << 2;

The above code will invoke the callback at index 0, 1, and 2 in the respective order.

In the case where the dispatcher takes a string as the index, the invoker can be passed strings.

typedef boost::dispatch::dispatcher < void (), std::string > string_dispatcher;
string_dispatcher d;
boost::dispatch::invoke_(d)()
   << "This" << "is" << "a" << "test";

Binding Arguments

Since the dispatcher can bind arguments to callbacks, the invoker should do the same. To perform this argument binding, the invoker instance implements the function operator (operator()) which can be used as shown below:

boost::dispatch::invoke_(d)(1) << 0 << 1 << 2;
boost::dispatch::invoke_(d)(3) << 1 << 0 << 2;

If there are more arguments supported by the dispatcher callbacks, then the dispatcher allows for binding arguments:

boost::dispatch::invoke_(d)(1, 2, 3.0) << 0 << 9 << 2;
boost::dispatch::invoke_(d)(2, 3, 4.0) << 3 << 5 << 9;

Dealing With Exceptions

There are at least two possible exceptions thrown when a dispatcher is indexed -- these are boost::dispatch::invalid_index and boost::dispatch::unregistered_handler. What happens when the following code throws an exception?

boost::dispatch::dispatcher<void (int, int)> d;
d[0] = function1;
d[2] = function2;
d[3] = function3;
boost::dispatch::invoke_(d)(0, 1) 
   << 0 << 1 << 2 << 3; // notice that d[1] is not defined

What will happen is, d[0] is invoked, then when d[1] is invoked an boost::dispatch::unregistered_handler exception is thrown -- and d[2] and d[3] are left un-invoked.

Accumulators

When the callbacks return values, it's a good idea to collect the information and then deal with the collection -- or deal with the values as they are returned. The following section shows how an aggregator can be used to collect the results of the invocations:

struct vector_accumulator {
  vector_accumulator(vector<int> & v) : _v(v) { };
  void operator() (int i) {
    _v.push_back(i);
  };
  vector<int> & _v;
};

//...
boost::dispatch::dispatcher<int ()> d;
d[0] = function1;
d[1] = function2;
d[2] = function3;
vector<int> v;
vector_accumulator va(v);
boost::dispatch::invoke_(d, boost::function<void(int)>(va))() << 0 << 1 << 2;

The above code will pass the individual results of d[0](), d[1](), and d[2]() into the boost::function<void(int)> wrapped functor va.

The requirement for an aggregator is that it should be a functor/function that returns void and takes one argument of the dispatcher's signature return type. So for boost::dispatch::dispatcher<int(std::string)>d;, the invoker can take a function which returns void and takes an int as an argument -- or a functor which implements voidoperator()(int).
Copyright 2006 ,2007 Dean Michael Berris

PrevUpHomeNext