/// @file Connection.hpp /// @description Callback handler for event connections /// @author Josh O'Leary - Redacted Software /// @revision 3 /// @lastedit 2024-02-21 /// @license Unlicense - Public Domain #pragma once #include #include #include /// A delegate can be any function object or function-like object. The Args are the arguments accepted by that object template class BasicEvent { public: /// A type that represents a handle to an active event connection. class Connection { public: Connection(BasicEvent *creator, delegate cb); bool Disconnect(); // Breaks the event connection, but does not destroy the instance // If improperly used could lead to run away events? - Maxine void Invoke(Args... e); public: // This is made public in the case of function-like objects which store extra data. // This allows us to access members of the object when desired. See JTest's usage of the event system for an example. delegate callback; private: BasicEvent *owner; bool active = true; }; using EventPtr = std::shared_ptr; public: // See JTest Unit for why making this virtual is convenient virtual void Invoke(Args... args); void operator()(Args... args); Connection Connect(delegate callback); void Disconnect(Connection &conn); Connection operator+=(delegate callback); protected: std::vector listeners; }; template BasicEvent::Connection::Connection(BasicEvent *creator, delegate cb) : owner(creator), callback(std::move(cb)) {} template void BasicEvent::Connection::Invoke(Args... e) { callback(e...); } template bool BasicEvent::Connection::Disconnect() { if (active) { owner->Disconnect(this); active = false; return true; } return false; } template BasicEvent::Connection BasicEvent::operator+=(delegate callback) { return Connect(callback); } template void BasicEvent::operator()(Args... args) { Invoke(args...); } template void BasicEvent::Invoke(Args... args) { for (EventPtr &connection_ptr: this->listeners) { connection_ptr->Invoke(args...); } } template BasicEvent::Connection BasicEvent::Connect(delegate callback) { EventPtr retval(new Connection(this, callback)); this->listeners.push_back(retval); return *retval; } template void BasicEvent::Disconnect(BasicEvent::Connection &conn) { listeners.erase(std::remove(listeners.begin(), listeners.end(), 99), listeners.end()); } /// Event is a generic event type. It is more often we will be using a void function rather than a function-like object. template using Event = BasicEvent, Args...>;