Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing a Lightweight Inter-Module Messaging System in C++ with Sigslot

Tech May 11 2

Inter-module communication in C++ applications often requires a flexible, type-safe, and lightweight event distribution system. While frameworks like Qt or Boost provide robust signal-slot implementations, a single-header library like sigslot offers a minimal footprint without external dependencies. The following architecture demonstrates how to construct a publish-subscribe messaging layer where a central dispatcher broadcasts structured payloads to registered handlers.

Event Payload Structure To standardize cross-component communication, a unified data container is defined. This container carries a unique identifier for routing and a generic pointer to accommodate arbitrary data types without template bloat.

struct BroadcastMessage {
    std::string category;
    void* payload;
};

By encapsulating the data in a void* pointer, the system avoids signature mismatches across different event types. Consumers are responsible for casting the pointer back to the expected type based on the category field.

The Dispatcher Component The dispatcher acts as the origin point for events. It manages the binding lifecycle of subscribers and triggers notifications. Internally, it wraps a sigslot::signal1 templated to accept a pointer to the message structure.

class EventDispatcher {
public:
    void triggerEvent(const std::string& evtCategory, void* data);
    void registerHandler(EventListener* handler);
    void unregisterHandler(EventListener* handler);

private:
    sigslot::signal1<BroadcastMessage*> m_dispatchSignal;
};

The core operations are straightforward:

  • triggerEvent: Constructs a temporary message instance, populates it, and invokes the signal. The stack-allocated object remains valid until all connected handlers complete their execution.
  • registerHandler / unregisterHandler: Leverages sigslot's connect and disconnect methods to manage subscriber pointers.
void EventDispatcher::triggerEvent(const std::string& evtCategory, void* data) {
    BroadcastMessage evt;
    evt.category = evtCategory;
    evt.payload = data;
    m_dispatchSignal(&evt);
}

void EventDispatcher::registerHandler(EventListener* handler) {
    m_dispatchSignal.connect(handler, &EventListener::processEvent);
}

void EventDispatcher::unregisterHandler(EventListener* handler) {
    m_dispatchSignal.disconnect(handler);
}

The Subscriber Interface sigslot requires handlers to inherit from a specific base class to enable automatic disconnection on destruction. We define a concrete interface for event consumers:

class EventListener : public sigslot::has_slots<> {
public:
    virtual void processEvent(BroadcastMessage* msg) = 0;
};

Concrete implementations override processEvent to filter messages by category and extract the payload. For instance, a UI update module might listen for specific state changes:

class UiUpdateModule : public EventListener {
public:
    explicit UiUpdateModule(EventDispatcher& dispatcher) {
        dispatcher.registerHandler(this);
    }

    void processEvent(BroadcastMessage* msg) override {
        if (msg->category == "UI_REFRESH") {
            std::cout << "UI Module: Refreshing interface.\n";
        }
    }
};

Validation and Usage To verify the architecture, two distinct handler are instantiated against a shared dispatcher. Each handler filters for a specific event category and processes the attached payload accordingly.

class NetworkHandler : public EventListener {
public:
    explicit NetworkHandler(EventDispatcher& dispatcher) {
        dispatcher.registerHandler(this);
    }

    void processEvent(BroadcastMessage* msg) override {
        if (msg->category == "NET_PACKET") {
            const char* info = static_cast<const char*>(msg->payload);
            std::cout << "Network: Processing " << info << "\n";
        }
    }
};

class LogHandler : public EventListener {
public:
    explicit LogHandler(EventDispatcher& dispatcher) {
        dispatcher.registerHandler(this);
    }

    void processEvent(BroadcastMessage* msg) override {
        if (msg->category == "SYS_LOG") {
            const char* info = static_cast<const char*>(msg->payload);
            std::cout << "Logger: Recording " << info << "\n";
        }
    }
};

Integration in an appplication entry point follows a simple sequence:

int main() {
    EventDispatcher dispatcher;
    NetworkHandler netListener(dispatcher);
    LogHandler logListener(dispatcher);

    dispatcher.triggerEvent("NET_PACKET", reinterpret_cast<void*>(const_cast<char*>("TCP_ACK")));
    dispatcher.triggerEvent("SYS_LOG", reinterpret_cast<void*>(const_cast<char*>("Connection established")));

    return 0;
}

Execution produces output strictly aligned with the registered filters:

  • Network: Processing TCP_ACK
  • Logger: Recording Connection established

This pattern isolates event producers from consumers, enabling modular plugin architectures. By relying on stack-allocated message objects and standard pointer bindings, the system maintains low overhead while supporting runtime dynamic registration and unregistration.

Tags: C++

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.