These templates are an alternative way to implement the callback design pattern. They're thread-safe, but they are optimized for the use case where the same execution thread always invokes the callbacks, and other execution threads' involvement is limited to installing the callbacks. These templates do not incur the overhead of weak containers. They use regular containers to store a collection of callbacks.
These templates provide a separate mcguffin for each callback, resulting in nearly identical semantics as weak containers. Uninstalling a callback is accomplished by destroying its mcguffin (with the same caveats as noted in the section called “Using callable objects to implement the callback design pattern”.
The three available templates are:
x::callback_list<signature
>
An unordered container for callbacks, stored in a
std::list
.
x::callback_map<key_type
,signature
>
The container for callbacks is a
std::map
.
x::callback_multimap<key_type
, signature
>
The container for callbacks is a
std::multimap
.
Each one of these is a
reference-counted object that needs to get
create
()d, first:
#include <x/callback_list.H> typedef x::callback_list<void (const std::string &)> list_callbacks_t; auto list_callbacks=list_callbacks_t::create();
This creates a container for callbacks that's based on a
std::list
: a simple list of callbacks.
Multiple callbacks can get installed in the list, with each callback
getting invoked consecutively.
The template parameter specifies the callbacks' signature. In this
case the callbacks take a single std::string
parameter, and they return void
.
#include <x/callback_map.H> #include <x/callback_multimap.H> typedef x::callback_map<int, void (const std::string &)> map_callbacks_t; typedef x::callback_multimap<int, void (const std::string &)> multimap_callbacks_t; auto map_callbacks=map_callbacks_t::create(); auto multimap_callbacks=multimap_callbacks_t::create();
This creates a container for callbacks that's based on a
std::map
or
std::multimap
. This results in multiple
callbacks getting invoked according in the key order.
The first template parameter is the callbacks' key type,
int
in this case.
The second parameter is the callback signature.
list_callbacks_t::base::mcguffin list_callback=list_callbacks->create_mcguffin( [] (const std::string &message) { // }); list_callback->install(); list_callbacks_t::base::mcguffin list_callback=list_callbacks->create_mcguffin_for( [] (const std::string &message) { // }); map_callbacks_t::base::mcguffin map_callback=map_callbacks->create_mcguffin_for( [] (const std::string &message) { // }, 4); if (!map_callback->installed()) { // ... } multimap_callbacks_t::base::mcguffin multimap_callback=multimap_callbacks->create_mcguffin_for( [] (const std::string &message) { // }, 4);
is a
mcguffin object which represents
a callable object.
container-type
::base::mcguffincreate_mcguffin
() creates a new mcguffin for
a callback to be added to the container.
Invoking the new mcguffin's install
()
method, which must be done at most once, installs the callback.
The callback will not be used until it gets
install
()ed.
create_mcguffin_for
() is equivalent to
using create_mcguffin_for
() immediately
followed by install
().
create_mcguffin_for
() returns
an already-install
()ed mcguffin.
A second parameter to install
() or
create_mcguffin_for
() specifies the new
callback's key, for a map or a multimap container.
installed
() indicates whether or not the
mcguffin object is installed in its container (the mcguffin cannot
be installed in a map if its key is already used by another callback,
of course).
Each mcguffin's callback remains installed until the mcguffin
gets destroyed.
Each mcguffin holds a reference on the callback container it was
created for. The callable object will be invoke
()
as long as the mcguffin exists. The callable object no longer
gets invoke
() after the last reference to
the mcguffin goes out of scope, and the mcguffin gets destroyed.
list_callbacks->invoke("Hello world");
All active callbacks, whose mcguffins have not been destroyed
(minding the race condition noted in the section called “Using callable objects to implement the callback design pattern”),
get invoked. Parameters supplied to invoke
()
get forwarded to each callback.
When the container's callbacks return a void
,
invoke
() invokes each installed callback
with no further processing.
All exceptions thrown by callbacks are
logged, and the next callback gets
invoked.
When callbacks return a value, two additional parameters must be
provided when create
()ing the container:
typedef x::callback_map<std::string, int(double)> my_callback_map; auto callbacks=my_callback_map::create( [] (int value) { return value < 0; }, [] { return -1; }); auto mcguffin=callbacks->create_mcguffin_for([] (double n) { int n=sign(n); return n; }, "Sign"); int result=callbacks->invoke(2.3);
This example defines a container for callbacks that take a
double
parameter and return an
int
.
When callbacks return values they get invoke
()d
as follows:
invoke
() returns the same type.
The first parameter to create
() is
a callable object that takes the same parameter, and which returns
a bool
.
The value returned from the callback is passed to this lambda.
If the lambda returns false
,
invoke
() does not invoke any more
callbacks, and returns the value that th callback returned.
If the lambda returns true
,
the next callback in the container gets
invoke
()d.
If there are no callbacks in the container, of if the first
lambda returned true
for every one
of them,
invoke
() calls the second lambda.
The second lambda must return the same type that the callbacks
return, and invoke
() returns this value.