Jordan Savant # Software Engineer

Event

This is an implemenation of an Event class in C++. Since C++ has no bult in event structure I created this one to facilitate the ability to assign listener functions for an arbitrary function definition.

It utilizes C++ Templates.

Implementation

Event.hpp

#pragma once
#ifndef BIT_EVENT_H
#define BIT_EVENT_H

#include <vector>

namespace bit
{
    template<typename T>
    class Event
    {
    private:
        std::vector<T> listeners;

    public:
        template<typename A>
        void trigger(A arg1)
        {
            for(unsigned int i=0; i < listeners.size(); i++)
                listeners[i](arg1);
        }
        template<typename A, typename B>
        void trigger(A arg1, B arg2)
        {
            for(unsigned int i=0; i < listeners.size(); i++)
                listeners[i](arg1, arg2);
        }
        template<typename A, typename B, typename C>
        void trigger(A arg1, B arg2, C arg3)
        {
            for(unsigned int i=0; i < listeners.size(); i++)
                listeners[i](arg1, arg2, arg3);
        }

        // Registers the listener and returns its ID if it needs to remove it
        unsigned int operator += (T listener)
        {
            listeners.push_back(listener);
            return listeners.size() - 1;
        }

        // Removes a listener by its id
        void operator -= (unsigned int index)
        {
            listeners.erase(listeners.begin() + index);
        }
    };
}

#endif

Example Usage

In a tile based game a Tile has an event called a onBodyEnter which should fire when a character moves onto the tile. Listeners can react to this event.

Tile.hpp

// event definition
bit::Event<std::function<void(Tile* t, Body* body)>> onBodyEnter;

Tile.cpp

// event firing, body is what has entered the tile
onBodyEnter.trigger<Tile*, Body*>(this, body);

So our tile has the event defined with the function definition that the listeners should be prepared for. When a body enters the tile it triggers the event which iterates its listeners and runs them with the tile and body in context.

A listener example is a Door object that sits adjacent to tiles. It wants to know if a body enters an adjacent tile so that it can open automatically.

Door.cpp

void Door::registerTileTriggers(Tile* tile)
{
    Door* d = this;

    if(tile)
    {
        tile->onBodyEnter += [d] (Tile* t, Body* b) {
            if(b->Body::schema.type == Body::Type::Character)
            {
                d->openerCount++;
                d->attemptOpen();
            }
        };
        tile->onBodyLeave += [d] (Tile* t, Body* b) {
            if(b->Body::schema.type == Body::Type::Character)
            {
                d->openerCount--;
                if(d->openerCount == 0)
                {
                    d->attemptClose();
                }
            }
        };
    }
}

The Door registers a listener for the adjacent tile. When fired the lambda operation will attempt to open the door that was listening.