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

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

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

template<typename TEvent>
class CSignal: protected CConnector {
  friend class CSlot<TEvent>;
public:
  // Con- & destructor
  CSignal() {};
  virtual ~CSignal() {};

  // Connects the given slot to this signal
  bool Connect(CSlot<TEvent>* poSlot) {
    return CConnector::Connect(poSlot);
  };

  // Disconnects the given slot from this signal
  bool Disconnect(CSlot<TEvent>* poSlot) {
    return CConnector::Disconnect(poSlot);
  };

  // Raises the given event to the connected slots
  typename TEvent::CEventResultType RaiseEvent(TEvent& oEvent) {
    // Process all slots
    auto ppoThisSlot = begin();
    auto ppoLastSlot = end();
    while (ppoThisSlot != ppoLastSlot) {
      // Let this slot process the event
      auto ppoNextSlot = ppoThisSlot;
      ++ppoNextSlot;
      CSlot<TEvent>* poThisSlot = (CSlot<TEvent>*)*ppoThisSlot;
      oEvent.AddResult(poThisSlot->OnEvent(oEvent));
      ppoThisSlot = ppoNextSlot;
    }

    // And return the result
    return oEvent.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

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

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

template<typename TEvent>
class CSlot: protected CConnector {
  friend class CSignal<TEvent>;
public:
  // Con- & destructor
  CSlot() {};
  virtual ~CSlot() {};

  // Connects the given slot to this signal
  bool Connect(CSignal<TEvent>* poSignal) {
    return CConnector::Connect(poSignal);
  };

  // Disconnects the given slot from this signal
  bool Disconnect(CSignal<TEvent>* poSignal) {
    return CConnector::Disconnect(poSignal);
  };

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

// Bridges the call to the slot to the method specified in the template
template<typename TEvent, typename TEventSink>
class CSlotBridge: public CSlot<TEvent> {
public:
  // Con- & destructor
  CSlotBridge(TEventSink* poSink):
   m_poSink(poSink) {
  };
  virtual ~CSlotBridge() {};

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

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

#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 CEventTypeBase {
public:
  // Return  type of the event
  typedef TValueType CEventResultType;

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

  // Adds the given result value
  virtual void AddResult(TValueType uValue) = 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 CEventTypeNoResult: public CEventTypeBase<bool> {
public:
  CEventTypeNoResult() {};
  virtual ~CEventTypeNoResult() {};
  virtual void AddResult(bool) {};
  virtual bool GetResult() {
    return false;
  };
  virtual void ResetResult() {};
};

// Default or-ed return value
template<typename TValueType, TValueType uStartValue>
class CEventTypeOr: public CEventTypeBase<TValueType> {
public:
  CEventTypeOr(): m_uValue(uStartValue) {};
  virtual ~CEventTypeOr() {};
  void AddResult(TValueType uValue) {
    m_uValue = m_uValue | uValue;
  };
  virtual TValueType GetResult() {
    return m_uValue;
  };
  virtual void ResetResult() {
    m_uValue = uStartValue;
  };

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

// Default And-ed return value
template<typename TValueType, TValueType uStartValue>
class CEventTypeAnd: public CEventTypeBase<TValueType> {
public:
  CEventTypeAnd(): m_uValue(uStartValue) {};
  virtual ~CEventTypeAnd() {};
  void AddResult(TValueType uValue) {
    m_uValue = m_uValue & uValue;
  };
  virtual TValueType GetResult() {
    return m_uValue;
  };
  virtual void ResetResult() {
    m_uValue = uStartValue;
  };

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

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

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

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

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

// String collection event; returns the last specified string
class CEventTypeStringLast: public CEventTypeBase<std::wstring> {
public:
  CEventTypeStringLast() {};
  virtual ~CEventTypeStringLast() {};
  void AddResult(std::wstring sValue) {
    m_sValue = sValue;
  };
  virtual std::wstring GetResult() {
    return m_sValue;
  };
  virtual void ResetResult() {
    m_sValue = L"";
  };

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

#endif // INCLUDE_TWOLOGS_COMMON_SIGNALSLOT_EVENT_H