Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bdlmt_signaler
[Package bdlmt]

Provide an implementation of a managed signals and slots system. More...

Namespaces

namespace  bdlmt

Detailed Description

Outline
Purpose:
Provide an implementation of a managed signals and slots system.
Classes:
bdlmt::Signaler a signaler
bdlmt::SignalerConnection signaler/slot connection
bdlmt::SignalerConnectionGuard RAII signaler/slot connection
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:
  class Button {
      // A pretend GUI button.

      // DATA
      int d_numPresses;

    public:
      // TYPES
      typedef bsl::function<void(int)> OnPressSlotType;
          // Slot argument is the number of times the button has been
          // pressed.

    private:
      // PRIVATE DATA
      bdlmt::Signaler<void(int)> d_onPress;
          // Signaler argument is the number of times the button has been
          // pressed.

    public:
      // CREATORS
      Button();
          // Construct a 'Button' object.

      // MANIPULATORS
      bdlmt::SignalerConnection onPressConnect(const OnPressSlotType& slot);
          // Connect the specified 'slot' to this button.

      void press();
          // Simulate user pressing on GUI button.
  };
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();