Skip navigation links
IT services and product development
Menu
TwoLogs
IT services and product development

Source code for C_SignalSlot

Download

The source files for this module are listed below.  You can also download the module C_SignalSlot as a zip archive; this archive contains all the source files and documentation.

Description

The signal/slot library allows you to use dynamically attached events in your code; Slots and signals can be connected and disconnected at any time.  The events themselves can be used to pass on information and cumulate return values.

Information

This source code also uses our CConnector class.  You can also download it at our website.

The signals and slots in this library are implemented as connectors.  See the CConnector class for more information on connectors.  A signal/slot pair is defined by the event type they handle; signals and slots can only be connected if they use the same event type.

You can use the CSignal class to raise events.  First you create the event parameters object which holds all the information for the event.  When you then call the RaiseEvent method and pass it the event object, all connected slots will be triggered via their OnEvent method.  Each slot can return a result value that will be cumulated into a final result value.  This result value is returned from the RaiseEvent method.

If you want to send events, derive your class from CSignal.  Use the desired event type as the template parameter for the CSignal base class.  You can also aggregate a CSignal as a member and call it's RaiseEvent function when needed; this however prevents immediate access to your signal by slots wanting to connect to it.

If you want to receive events, you have to derive your class from CSlot.  Use the desired event type as the template parameter for the CSlot base class.  Also implement the implied OnEvent function to enable receiving these events.  You can hook up to signals by Connect-ing to them when needed.  When processing an event in a slot, just return your slot's reaction from the OnEvent function; the aggregated result will be returned from the call to the RaiseEvent function in the signal that raised the event.

When you have both base classes and derived classes that are interested in receiving the same event, you cannot use the virtual inheritance mechanism anymore that is used by the OnEvent method of the CSlot class.  Instead, you must use the class CSlotBridge.  Either directly derive from it, or aggregate it as a member variable.  In this case, make sure you implement a function called OnBridgedEvent and not OnEvent.  The signature for the OnBridgedEvent is the same as for OnEvent.  The CSlotBridge class expects you to define which object handles the OnBridgedEvent call.

The event object type must contain all the information you want to send in the event.  It is therefore easier to define it as a struct.  You must also derive the event struct from either the CEventTypeBase base class or one of the predefined CEventTypeBase derived classes listed above.  These base classes allow smart aggregation of event results; when your signal gets raised to two slots, their individual results need to be merged.  The exact result type can also be specified as a parameter to some of these base classes.  A value of this event type must also be returned from the OnEvent and/or OnBridgedEvent method in the slots that handle the event.  The RaiseEvent method on the signal returns the aggregated result from all slots.

There are already six predefined base event types;

  • CEventTypeNoResult can be used for events which do not need a return value (you must still return a bool value but it is ignored).
  • CEventTypeAnd / CEventTypeOr can be used to cumulate the different result values by using bitwise 'and' and 'or' operations.  You must define the integral type to use as a template parameter, along with it's initial value.
  • CEventTypeBoolAnd / CEventTypeBoolOr can be used to cumulate the different result values by using logical 'and' and 'or' operations.  The starting value for the 'and' version is true, the starting value for the 'or' version is false.  The OnEvent and OnBridgedEvent methods need to return a bool value.
  • CEventTypeStringLast will remember the string specified by the last slot that is signalled.  Note that return values from slots that got signalled before the last slot are lost.

Files

Each file belonging to this source code module is listed below.

Signal.h

/*******************************************************************************

  Version: 3
  Author:  Carl Colijn, TwoLogs
  Contact: c.colijn@twologs.com
  Source:  https://www.twologs.com/sourcecode

  This code is freely distributable, as long as this comment remains intact.
  If you find this source useful, you may use this code in your own projects
  free of charge, but some acknowledgement to the author of this code is always
  appreciated :)
  The source is however distributed 'as is' without waranty and/or support, and
  may not be fit for each and every application.  Use it at your own discretion
  and at your own risk.
  The source already has undergone testing.  This doesn't mean however that all
  bugs are removed from this piece of code.  If you find one of them, please
  contact me about it.  I can however not guarantee when and if the bug will be
  fixed.

  More information about this module can be found in the accompanying HTML file.

*******************************************************************************/

#ifndef INCLUDE_TWOLOGS_COMMON_SIGNALSLOT_SIGNAL_H
#define INCLUDE_TWOLOGS_COMMON_SIGNALSLOT_SIGNAL_H

// TSlot predefine to break circular reference
template<typename TEvent>
class TSlot;

#include "../C_Connector/Connector.h"
#include "Slot.h"
#include "Event.h"

template<typename TEvent>
class TSignal: protected TConnector {
  friend class TSlot<TEvent>;
public:
  // Con- & destructor
  TSignal() {};
  virtual ~TSignal() {};

  // Connects the given slot to this signal
  bool Connect(TSlot<TEvent>* slotPtr) {
    return TConnector::Connect(slotPtr);
  };

  // Disconnects the given slot from this signal
  bool Disconnect(TSlot<TEvent>* slotPtr) {
    return TConnector::Disconnect(slotPtr);
  };

  // Raises the given event to the connected slots
  typename TEvent::TEventResultType RaiseEvent(TEvent& event) {
    // Process all slots
    auto thisSlotPtrPtr = begin();
    auto lastSlotPtrPtr = end();
    while (thisSlotPtrPtr != lastSlotPtrPtr) {
      // Let this slot process the event
      auto nextSlotPtrPtr = thisSlotPtrPtr;
      ++nextSlotPtrPtr;
      TSlot<TEvent>* thisSlotPtr = (TSlot<TEvent>*)*thisSlotPtrPtr;
      event.AddResult(thisSlotPtr->OnEvent(event));
      thisSlotPtrPtr = nextSlotPtrPtr;
    }

    // And return the result
    return event.GetResult();
  };
};

#endif // INCLUDE_TWOLOGS_COMMON_SIGNALSLOT_SIGNAL_H

Slot.h

/*******************************************************************************

  Version: 3
  Author:  Carl Colijn, TwoLogs
  Contact: c.colijn@twologs.com
  Source:  https://www.twologs.com/sourcecode

  This code is freely distributable, as long as this comment remains intact.
  If you find this source useful, you may use this code in your own projects
  free of charge, but some acknowledgement to the author of this code is always
  appreciated :)
  The source is however distributed 'as is' without waranty and/or support, and
  may not be fit for each and every application.  Use it at your own discretion
  and at your own risk.
  The source already has undergone testing.  This doesn't mean however that all
  bugs are removed from this piece of code.  If you find one of them, please
  contact me about it.  I can however not guarantee when and if the bug will be
  fixed.

  More information about this module can be found in the accompanying HTML file.

*******************************************************************************/

#ifndef INCLUDE_TWOLOGS_COMMON_SIGNALSLOT_SLOT_H
#define INCLUDE_TWOLOGS_COMMON_SIGNALSLOT_SLOT_H

// TSignal predefine to break circular reference
template<typename TEvent>
class TSignal;

#include "../C_Connector/Connector.h"
#include "Signal.h"
#include "Event.h"

template<typename TEvent>
class TSlot: protected TConnector {
  friend class TSignal<TEvent>;
public:
  // Con- & destructor
  TSlot() {};
  virtual ~TSlot() {};

  // Connects the given slot to this signal
  bool Connect(TSignal<TEvent>* signalPtr) {
    return TConnector::Connect(signalPtr);
  };

  // Disconnects the given slot from this signal
  bool Disconnect(TSignal<TEvent>* signalPtr) {
    return TConnector::Disconnect(signalPtr);
  };

  // Triggered when the signal fires the event
  virtual typename TEvent::TEventResultType OnEvent(TEvent& event) = 0;
};

// Bridges the call to the slot to the method specified in the template
template<typename TEvent, typename TEventSink>
class TSlotBridge: public TSlot<TEvent> {
public:
  // Con- & destructor
  TSlotBridge(TEventSink* sinkPtr):
   m_sinkPtr(sinkPtr) {
  };
  virtual ~TSlotBridge() {};

private:
  // The event sink to route messages to
  TEventSink* m_sinkPtr;

  // Triggered when the signal fires the event
  typename TEvent::TEventResultType OnEvent(TEvent& event) {
    return m_sinkPtr->OnBridgedEvent(event);
  };
};

#endif // INCLUDE_TWOLOGS_COMMON_SIGNALSLOT_SLOT_H

Event.h

/*******************************************************************************

  Version: 3
  Author:  Carl Colijn, TwoLogs
  Contact: c.colijn@twologs.com
  Source:  https://www.twologs.com/sourcecode

  This code is freely distributable, as long as this comment remains intact.
  If you find this source useful, you may use this code in your own projects
  free of charge, but some acknowledgement to the author of this code is always
  appreciated :)
  The source is however distributed 'as is' without waranty and/or support, and
  may not be fit for each and every application.  Use it at your own discretion
  and at your own risk.
  The source already has undergone testing.  This doesn't mean however that all
  bugs are removed from this piece of code.  If you find one of them, please
  contact me about it.  I can however not guarantee when and if the bug will be
  fixed.

  More information about this module can be found in the accompanying HTML file.

*******************************************************************************/

#ifndef INCLUDE_TWOLOGS_COMMON_SIGNALSLOT_EVENT_H
#define INCLUDE_TWOLOGS_COMMON_SIGNALSLOT_EVENT_H

#include <string>

// Base for all event types
template<typename TValueType>
class TEventTypeBase {
public:
  // Return  type of the event
  typedef TValueType TEventResultType;

  // Con- & destructor
  TEventTypeBase() {};
  virtual ~TEventTypeBase() {};

  // Adds the given result value
  virtual void AddResult(TValueType value) = 0;

  // Gives the final, overall, result value
  virtual TValueType GetResult() = 0;

  // Resets the result value
  virtual void ResetResult() = 0;
};

// Events without return values
// Note that the OnEvent function signature must return bool, but you do not
// need to return anything.
class TEventTypeNoResult: public TEventTypeBase<bool> {
public:
  TEventTypeNoResult() {};
  virtual ~TEventTypeNoResult() {};
  virtual void AddResult(bool) {};
  virtual bool GetResult() {
    return false;
  };
  virtual void ResetResult() {};
};

// Default or-ed return value
template<typename TValueType, TValueType startValue>
class TEventTypeOr: public TEventTypeBase<TValueType> {
public:
  TEventTypeOr(): m_value(startValue) {};
  virtual ~TEventTypeOr() {};
  void AddResult(TValueType value) {
    m_value = m_value | value;
  };
  virtual TValueType GetResult() {
    return m_value;
  };
  virtual void ResetResult() {
    m_value = startValue;
  };

private:
  // The overall value
  TValueType m_value;
};

// Default And-ed return value
template<typename TValueType, TValueType startValue>
class TEventTypeAnd: public TEventTypeBase<TValueType> {
public:
  TEventTypeAnd(): m_value(startValue) {};
  virtual ~TEventTypeAnd() {};
  void AddResult(TValueType value) {
    m_value = m_value & value;
  };
  virtual TValueType GetResult() {
    return m_value;
  };
  virtual void ResetResult() {
    m_value = startValue;
  };

private:
  // The overall value
  TValueType m_value;
};

// Default boolean or-ed return value;
// returns if any slot returned True
class TEventTypeBoolOr: public TEventTypeBase<bool> {
public:
  TEventTypeBoolOr(): m_value(false) {};
  virtual ~TEventTypeBoolOr() {};
  void AddResult(bool value) {
    m_value = m_value || value;
  };
  virtual bool GetResult() {
    return m_value;
  };
  virtual void ResetResult() {
    m_value = false;
  };

private:
  // The overall value
  bool m_value;
};

// Default boolean And-ed return value;
// returns if all slots returned True
class TEventTypeBoolAnd: public TEventTypeBase<bool> {
public:
  TEventTypeBoolAnd(): m_value(true) {};
  virtual ~TEventTypeBoolAnd() {};
  void AddResult(bool value) {
    m_value = m_value && value;
  };
  virtual bool GetResult() {
    return m_value;
  };
  virtual void ResetResult() {
    m_value = true;
  };

private:
  // The overall value
  bool m_value;
};

// String collection event; returns the last specified string
class TEventTypeStringLast: public TEventTypeBase<std::wstring> {
public:
  TEventTypeStringLast() {};
  virtual ~TEventTypeStringLast() {};
  void AddResult(std::wstring value) {
    m_value = value;
  };
  virtual std::wstring GetResult() {
    return m_value;
  };
  virtual void ResetResult() {
    m_value = L"";
  };

private:
  // The last specified value
  std::wstring m_value;
};

#endif // INCLUDE_TWOLOGS_COMMON_SIGNALSLOT_EVENT_H