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";
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;
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.
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 void operator() (int) . |
Copyright © 2006 ,2007 Dean Michael Berris |