Mmcshane Eventbus Save

A threadsafe C++ implementation of the EventBus idiom

Project README

EventBus

It's an event bus. Subscribe to events. Publish events. Quoting the documentation for Google's implementation of the EventBus concept in Guava:

EventBus allows publish-subscribe-style communication between components without requiring the components to explicitly register with one another (and thus be aware of each other). It is designed exclusively to replace traditional […] in-process event distribution using explicit registration. It is not a general-purpose publish-subscribe system, nor is it intended for interprocess communication.

Caution: there are downsides to every pattern or idiom. EventBuses are no different. Read about some of the down-sides and alternatives and really think about if this is an approach you want to adopt.

Features

This library is fairly small and being header-only should be easy to integrate into existing projects. A C++11 compiler is required. All operations are threadsafe -- establishing and/or ending subscriptions concurrent with event publication is safe for any number of threads. Moreover, event publication is wait-free within the event bus. It may be the case that event handlers are not wait-free.

Event subscriptions are aware of event-type polymorphism. That is to say that given a pair of event types called Base and Derived where Base is a base class of Derived, a subscription to Base event types will be invoked when an event of type Derived is published. This is accomplished with a slight augmentation to normal C++ inheritance -- Derived must extend Base via the mpm::enable_polymorphic_dispatch class rather than directly. Rather than the following "normal" inheritance:

struct Derived : Base
{
}

Derived should be defined as

struct Derived : mpm::enable_polymorphic_dispatch<Derived, Base>
{
}

Defining Derived in this manner will allow and event of this type to be delivered as both Derived and Base.

It is not required that mpm::enable_polymorphic_dispatch be used. Any instance of a C++ object type can be published, however if polymorphic delivery is desired then mpm::enable_polymorphic_dispatch must be used.

Example Usage

Let's define some events

struct my_base_event : mpm::enable_polymorphic_dispatch<my_base_event>
{
    int x = 12;
};

struct my_derived_event
    : mpm::enable_polymorphic_dispatch<my_derived_event, my_base_event>
{
    int y = 5;
};

struct my_object {};

struct my_non_polymorphic_event : my_object
{
    int foo = -1;
};

Here's a quick look at publishing and subscribing to a polymorphic event

mpm::eventbus ebus;

// two subscriptions - 1 for my_base_event and 1 for my_derived_event

auto base_subscription = mpm::scoped_subscription<my_base_event> {
    ebus, [](const my_base_event& mbe) noexcept {
        std::cout << "handling a base event" << mbe.x;
    }
);

auto derived_subscription = mpm::scoped_subscription<my_derived_event> {
    ebus, [](const my_derived_event& mde) noexcept {
        std::cout << "handling a derived event" << mde.y;
    }
);

// publish
ebus.publish(my_derived_event{});

// subscriptions terminate at scope exit

Some things worth noting here

  • Both event handlers will fire
  • If you have a C++14 compiler, the callback can be declared with auto (e.g. const auto& event), removing the duplication in specifying the event type.

For non-polymorphic dispatch, any object type can be published and it will be handled by handlers for only that exact type.

mpm::eventbus ebus;

// two subscriptions - 1 for my_object, 1 for my_non_polymorphic_event

auto base_subscription = mpm::scoped_subscription<my_object> {
    ebus, [](const my_object& mo) noexcept {
        std::cout << "handling a my_object";
    }
};

auto non_poly_subscription = mpm::scoped_subscription<my_non_polymorhpic_event> {
    ebus, [](const my_non_polymorphic_event& mnpe) noexcept {
        std::cout << "handling a my_non_polymorphic_event " << mnpe.foo;
    }
);

// publish
ebus.publish(my_non_polymorphic_event{});

// subscriptions terminate at scope exit

Note with the above example that only the handler for my_non_polymorphic_event will fire because the inheritance relationship was not established via mpm::enable_polymorphic_dispatch.

Building

There's really no build needed as this is a header-only library, however if you want to run the unit tests or generate docs you can use the cmake build. To perform an out-of-tree build

$ cd "$(mktemp -d)"
$ cmake /path/to/eventbus/repository
$ make && make test
$ make docs # generates doxygen documentation under $PWD/docs

TODO

  • As it stands, publishing events from within event handlers is allowed. It's not clear that this is good.
  • Implement an asynchronous publication proxy
  • Maybe allow non-noexcept subscribers but unsubscribe them if they throw?
  • Pull leftright as an external project rather than embedding it under thirdparty/
Open Source Agenda is not affiliated with "Mmcshane Eventbus" Project. README Source: mmcshane/eventbus
Stars
32
Open Issues
0
Last Commit
4 years ago
Repository

Open Source Agenda Badge

Open Source Agenda Rating