Single execution thread callback container

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);

container-type::base::mcguffin is a mcguffin object which represents a callable object. create_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: