BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bdlmt_signaler

Detailed Description

Outline

Purpose

Provide an implementation of a managed signals and slots system.

Classes

Description

This component provides the template class bdlmt::Signaler<t_PROT>, an implementation of a managed signal and slots system for the void returning function prototype t_PROT. Each signaler represents a callback with multiple targets (called "slots") which are invoked in a known order when the signaler is invoked (called being "emitted").

A slot being connected to a signaler is represented by a bdlmt::SignalerConnection which can be used to disconnect that connection at any time, but can also be discarded if managing the lifetime of the individual connection is not needed. A guard to disconnect a slot on its destruction is available in bdlmt::SignalerConnectionGuard.

Signalers and the slots connected to them are all managed. Any connections will be automatically disconnected when a Signaler is destroyed, or when explicitly disconnected, and all internally allocated resources will be destroyed when no more references to them remain. This enables the user to make signaler/slot connections and emit signals without expanding effort on managing the lifetimes of any of the involved objects.

Slot Object Requirements

Slots connected to a signaler Signaler<t_PROT> must be callable and copyable objects that may be passed to the constructor of bsl::function<t_PROT>. I.e. a slot must be callable with the same arguments as t_PROT, and if a slot returns a value it will be discarded.

Call Groups

Slots are free to have side effects, which means that some slots may have to be called before others even if they are not connected in that order. bdlmt::Signaler allows slots to be placed into groups that are ordered in some way. Group values are integers, and are ordered by the integer < relation. By default, all connected slots have the group value set to 0.

Concurrency and Order of Execution

Within a single thread of execution slots are always executed in the order defined by their respective groups and, within groups, by the order they were connected to the signaler. If the signaler's call operator is invoked concurrently from multiple threads, slots may also be executed concurrently.

Slots Lifetime

Internally, bdlmt::Signaler stores copies of connected slot objects. The copy of the slot object is destroyed after the slot is disconnected from the signaler, or after the signaler is destroyed, but the exact moment is unspecified. It is only guaranteed that the lifetime of such object will not exceed the collective lifetime of the signaler and all connection objects associated with to that signaler.

Comparison of SignalerConnections

Ordering comparisons of SignalerConnections are transitive and are provided to facilitate their being stored in an associative container. The ordering of a SignalerConnection does not change when it is disconnected.

In equality comparisons, two default constructed connections compare equivalent and a default constructed connection is never equivalent to a connection to a slot. If a connection is not default constructed, it is equivalent only to another connection that refers to the same slot.

Thread Safety

bdlmt::Signaler is fully thread-safe, meaning that multiple threads may use their own instances of the class or use a shared instance without further synchronization.

With the exception of assignment operators, swap(), reset() and release() member functions, bdlmt::SignalerConnection and bdlmt::SignalerConnectionGuard are thread-safe, meaning that multiple threads may use their own instances of the class or use a shared instance without further synchronization.

It is safe to access or modify two distinct connection objects simultaneously, each from a separate thread, even if they represent the same slot connection.

Usage

Suppose we want to implement a GUI button class that allows users to keep track of its press events.

First, we declare the class:

/// A pretend GUI button.
class Button {
// DATA
int d_numPresses;
public:
// TYPES
/// Slot argument is the number of times the button has been pressed.
typedef bsl::function<void(int)> OnPressSlotType;
private:
// PRIVATE DATA
/// Signaler argument is the number of times the button has been
/// pressed.
bdlmt::Signaler<void(int)> d_onPress;
public:
// CREATORS
/// Construct a `Button` object.
Button();
// MANIPULATORS
/// Connect the specified `slot` to this button.
bdlmt::SignalerConnection onPressConnect(const OnPressSlotType& slot);
/// Simulate user pressing on GUI button.
void press();
};
Definition bdlmt_signaler.h:1133
Definition bdlmt_signaler.h:975
Forward declaration.
Definition bslstl_function.h:934

Then, we define its methods:

// CREATORS
Button::Button()
: d_numPresses(0)
{
}
// MANIPULATORS
bdlmt::SignalerConnection Button::onPressConnect(
const OnPressSlotType& slot)
{
return d_onPress.connect(slot);
}
void Button::press()
{
d_onPress(++d_numPresses);
}

Next, we provide an event handler callback printing its argument, which the class will pass the number of times the button has been pressed:

void showPresses(int numPresses)
{
bsl::cout << "Button pressed " << numPresses << " times.\n";
}

Then, in main, create a button and subscribe to its events.

u::Button button;
bdlmt::SignalerConnection connection = button.onPressConnect(
&u::showPresses);

Next the button is "pressed", we will receive a notification.

button.press();

Now, we see the following message:

Button pressed 1 times.

Finally, unsubscribe from button's events when we don't want to receive notifications anymore. (If we didn't call disconnect, button would clean up all the allocated resources when it went out of scope):

connection.disconnect();
void disconnect() const BSLS_KEYWORD_NOEXCEPT