Easy to use C++ thread-based event system
xxHash, but you can change the hash function for strings in Event/common.cpp
, Event::HashId::hashString
xxhash.hpp
from https://github.com/RedSpah/xxhash_cpp included
Add the Event
folder to your includes.
OR use the precompiled part from the includes
folder.
#include <iostream>
//include the event management class with...
#include "Event/Emitter.h"
//or #include "include/EventEmitter.h"
//since the events are multithreaded, I highly recommend using mutexes
#include <mutex>
std::mutex m;
void listener(Event::DataPack* data){
m.lock();
std::cout << "i'm alive!" << std::endl;
m.unlock();
//stop event processing
Event::terminate();
}
int main(){
//do something...
//initialize event loops
Event::init();
//An Event namespace has a predefined Object of the class Event::Emitter, it called Event::root.
//You can call methods directly by pointing this object,
//also you can call Event::[method name] to refer to it.
//Ok, create the listener
Event::on("someEvent", listener);
//now we can emit that event
Event::emit("someEvent");
//wait for the termination function (Event::terminate) to be called
Event::wait();
//do something...
return 0;
}
Also you can check main.cpp
for example.
- Adds listener to the event
- Adds listener to the event, but it will be executed only once
- Removes the listener
- Adds the listeners to the execution queue
if you want to pass some data in the function - just use DataPack
/DataPackCast
/DataPackAutoClean
- you don't need to delete the data packs manually
- you can pass ONLY dynamically created packs (by
new
etc.)
//listener have to take only one argument with type of DataPack*:
void lis(DataPack*){
//now you can cast the data pack
DataPackCast<string> *pack = (DataPackCast<string>)DataPack;
//and use the data
cout << pack->data << endl;
}
//.....
//to pass the data:
myEvent.emit("someEvent", new DataPackCast<string>("my message"));
|
^--- this pack will be deleted after
all listeners of this event are executed
//that's why if you want to share one resource, you have to use pointers in your packs
(only public methods, fields etc.)
Event/ThreadLoops.h:
Event::defaultListenerFn_t = std::function<void(DataPack*)>
Event/Emitter.h:
Event::mainListenersMap_t = std::map<elemIdNum_t, std::vector<defaultListenerFn_t*>>
Event/common.h
Event::elemIdNum_t = unsigned long long
Event/ThreadLoops.h
static bool ThreadLoops::stop //// becomes true after executing ThreadLoops::terminate();
static void ThreadLoops::addExecutable(DataPack* data, anyof fn) //// add new functions into execution stack
data - some wrapped data
fn - one of the following:
const defaultListenerFn_t
std::stack<defaultListenerFn_t>
std::vector<defaultListenerFn_t>
std::vector<defaultListenerFn_t*>
static void ThreadLoops::init([size_t threadsNumber]) //// threads initialization
threadsNumber - number of threads to be created
if the parameter is not passed, it will use the number of cores
static void ThreadLoops::wait() //// wait for the termination function (ThreadLoops::terminate) to be called
static void ThreadLoops::terminate(bool dontWaitFunctions = false) //// stop all threads.
dontWaitFunctions - if false => wait until the last functions complete
if true => functions in the threads are still executing, but you can exit the program
Event/Emitter.h
Emitter* Emitter::on(HashId id, const defaultListenerFn_t& fn [, defaultListenerFn_t*& listenerPtr]) //// bind new listener function
id - HashId/string/char[]/elemIdNum_t - name of event
fn - function
listenerPtr - if passed, takes a pointer to a new function in the storage.
It can be useful if a lambda was passed as the argument "fn",
then you can remove such listeners with Emitter::off only by this pointer
Emitter* off(HashId id, const defaultListenerFn_t& fn) //// remove the listener
id - HashId/string/char[]/elemIdNum_t - name of event
fn - function
Emitter* emit(HashId id, DataPack* data = nullptr) //// emit some event
id - HashId/string/char[]/elemIdNum_t - name of event
data - the DataPack wrapper with some data
Emitter Event::root //// 'extern' defined instance of the Emitter
void Event::init(...) //// ref to ThreadLoops::init
void Event::wait(...) //// ref to ThreadLoops::wait
void Event::terminate(...) //// ref to ThreadLoops::terminate
void Event::on(...) //// ref to Event::root.on
void Event::off(...) //// ref to Event::root.off
void Event::emit(...) //// ref to Event::root.emit
Event/common.h
static elemIdNum_t HashId::hashString(anyof data) //// create a hash
data - one of the following:
std::string
const char*
elemIdNum_t HashId::data = 0 //// contains the hash
string HashId::src = "" //// source string of the hash
HashId::HashId(anyof data) //// constructors
data - one of the following:
elemIdNum_t
std::string
const char*
HashId
const HashId
HashId operators: =, !=, ==
types for all ones:
elemIdNum_t
std::string
const char*
HashId
const HashId
template<typename T, typename... U> bool Event::fnCanBeCompared(const std::function<T(U...)>& f)
////check if a function can be compared by Event::comparableCompare
template<typename T, typename... U> bool Event::comparableCompare(const std::function<T(U...)>& f1, const std::function<T(U...)>& f2)
////only for the comparable functions. if f1 == f2 => true
template<typename T, typename... U> bool compareFn(const std::function<T(U...)>& fnA, const std::function<T(U...)>& fnB)
////truth map:
if fnA and fnB are comparable: return fnA == fnB
if only one is comparable: return false
if both are uncomparable: return &fnA == &fnB
Event/DataPack.h
//// class DataPack
void* DataPack data //// data field
DataPack::DataPack(void* d) ////constructor
DataPack* copy() //// returns a copy of this pack with the same 'data' field
//// template<typename T = void*> class DataPackCast : public DataPack
////
//// It is similar to DataPack but with a template for the data type:
T DataPackCast::data
DataPackCast::DataPackCast(T d)
DataPackCast* copy()
//// template<typename T> class DataPackAutoClean : public DataPack
////
//// Uses a pointer to T (T*).
//// Automatically executes 'delete' on its data ONLY in the case when all copies of this object have also been deleted
//// (all the copies have a field that contains their total number)
T* DataPackAutoClean::data
DataPackAutoClean::DataPackCast(T* d)
DataPackAutoClean* copy()