HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

C++ 14 event bus implementation

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
implementationbusevent

Problem

The code allows you to subscribe to events and publish events via an EventBus class. Events are identified by a source and subject. Events can have a single parameter or they can be 'indexed' and have two parameters (the first being an integer index - I will be using this a lot so wanted a specialization).

I don't particularly like the fact that:

  • I need to have an IndexedSubscriptionmethod - it would be nicer to overload the Subscription method, but the compiler doesn't like that.



  • The template argument needs to be specified when calling Subscription and IndexedSubscription.



I would like to know if there are any improvements I could make (I am reasonably new to C++ but am experienced in other languages).

Example use

#include 
#include 
#include "EventBus.h"

class Sources
{
public:
    static EventSource Source1()
    {
        static EventSource source;

        return source;
    }

    static EventSource Source2()
    {
        static EventSource source;

        return source;
    }
};

class Subjects
{
public:
    static EventSubject Subject1()
    {
        static EventSubject subject;

        return subject;
    }
};

int main()
{
    EventBus eb;

    eb.Subscribe(Sources::Source1(), Subjects::Subject1(), [](double& data)
    {
        std::cout (Sources::Source1(), Subjects::Subject1(), 33);

    std::cout << "Done... ";
    std::getchar();

    return EXIT_SUCCESS;
}


Main event bus source

```
#pragma once

#include
#include
#include
#include
#include

template
using EventHandler = std::function;

template
using IndexedEventHandler = std::function;

template
using EventDataType = typename std::remove_reference::type>::type;

class EventIdentifier
{
private:
int _id;

public:
EventIdentifier()
{
static std::atomic_int counter = 1;

_id = counter++;
}

bool operator ==(const EventIdentifier &other) const
{
return _id == other._id;
}

bool operator
class EventSubject fi

Solution

Missing includes

The header needs

#include 
#include 


And the demo program needs

#include 


(or - better - just omit the final std::getchar() whose result is never used and whose error is ignored)

It's not clear why EventDataType removes references twice - C++ doesn't have references to references.

Instead of transforming types like that, it's probably better to constrain the template functions to reject reference types as arguments.

IndexedData::_data looks very dangerous, as it's a reference that's initialised from an rvalue-reference argument, so could very easily be invalidated.

I don't like laundering event data through void*, but you seem to have protected against accidents by carrying the type with the Subject class.

I think we could avoid casting, by using separate maps for subscriptions of different types.

EventBus::EventObserver has a virtual function, so we should provide it with a public virtual destructor for safety. Also, I don't think it makes sense for the Notify to modify the observer itself:

struct EventObserver
    {
        virtual ~EventObserver() = default;
        virtual void Notify(void* data) const = 0;
    };


Consider making EventBus::EventSubscription final, which could improve the compiler's ability to optimise.

I see no way to unsubscribe from this publisher. That's problematic, particularly if we have subscriptions which have captured references.

Instead of the separate Indexed versions of functions, consider generalising to take any number of data arguments, and forwarding them as a parameter pack. That reduces code and provides more user flexibility!

The test program looks very long-winded when creating source and subject identifiers. It can be simplified:

namespace Sources
{
    const EventSource Source1;
    const EventSource Source2;
}

namespace Subjects
{
    const EventSubject Subject1;
}


We needn't worry about the exact order in which these globals are initialised; any order is fine.

Consider adding names to our sources and subjects, because debugging gets hard when events are only identified by integers (and not easily predictable ones at that!).

The test program shouldn't exit with a partially-written line of output - add \n to the "Done" string.

Code Snippets

#include <type_traits>
#include <utility>
#include <cstdio>
struct EventObserver
    {
        virtual ~EventObserver() = default;
        virtual void Notify(void* data) const = 0;
    };
namespace Sources
{
    const EventSource Source1;
    const EventSource Source2;
}

namespace Subjects
{
    const EventSubject<double> Subject1;
}

Context

StackExchange Code Review Q#109987, answer score: 2

Revisions (0)

No revisions yet.