I posted an earlier version of this code a couple of days ago, so it's the updated version of the code posted here.
#include <iostream>
#include <functional>
#include <conio.h>
#include <unordered_map>
#include <string>
class EventListenerHandle
{
template <typename U>
friend class EventChannel;
private:
std::function<void()> removeFunction;
bool removed;
EventListenerHandle() = delete;
inline EventListenerHandle(std::function<void()> remove_function) :removed(false)
{
this->removeFunction = remove_function;
}
public:
inline void remove()
{
this->removed = true;
removeFunction();
}
};
template <typename T>
class EventChannel
{
private:
using idType = unsigned int;
static const idType& getNextId()
{
static idType id = 0;
return id++;
}
static std::unordered_map<idType, std::function<bool(const T&)>>& getHandlers()
{
static std::unordered_map<idType, std::function<bool(const T&)>> handlers;
return handlers;
}
static void removeListener(idType listener_id)
{
getHandlers().erase(listener_id);
}
public:
static EventListenerHandle registerListener(std::function<bool(const T&)> listener)
{
auto& id = getNextId();
getHandlers()[id] = listener;
return EventListenerHandle(std::bind(EventChannel<T>::removeListener, id));
}
static void tringgerEvent(T e)
{
for (const auto& h : getHandlers())
{
h.second(e);
}
}
};
/*to force the caller to pass the bare type (not pointer or reference) as the templapte parameter
if these where not here one could (and possibly would) write some code like:
EventChannel<TouchEvent&>::registerListener([](const TouchEvent& ev)->bool
{
});
TouchEvent t;
DISPATCH_EVENT(t);
and expect the listener to be called. so it's better to prevent them from
using pointer and reference types as template parameter for EventChannel class.
also i decided not to care for volatile types.
*/
template<typename T>
class EventChannel<T*>{};
template<typename T>
class EventChannel<T&>{};
template <typename T>
class EventChannel<const T>{};
//just for convenience
template <typename T>
void DISPATCH_EVENT(const T& t)
{
EventChannel<T>::tringgerEvent(t);
//std::cout << t.eventType();
}
//to let the caller pass pointers as the event object
template <typename T>
void DISPATCH_EVENT(T* t)
{
DISPATCH_EVENT<T>(*t);
}
#define EVET_TYPE_FUNC(T) static std::string eventType(){return #T;}
#define KDFA_DEFINE_VOID_EVENT(__NAME__) struct __NAME__{EVET_TYPE_FUNC(__NAME__)};
struct TouchEvent
{
int t_id;
float x;
float y;
EVET_TYPE_FUNC(TouchEvent);
};
KDFA_DEFINE_VOID_EVENT(CAMERA_MOVED_OR_ZOOMED);
void main()
{
auto lid = EventChannel<TouchEvent>::registerListener([](const TouchEvent& ev)->bool
{
std::cout << "got touch " << ev.t_id << "\n";
return false;
});
TouchEvent t;
t.t_id = 10;
DISPATCH_EVENT(t);//dispatch with object
DISPATCH_EVENT(&t);// dispatch with pointer
//it's best to use RAII to correctly call remove on event listener handles
lid.remove();//remove listener
lid.remove();//calling remove multiple times would not cause any problems. this is a mere design decision
DISPATCH_EVENT(t);
_getch();
}
consider reading the main
function first, it's pretty simple to understand. As I lost the ability to think about this design out of the box, any comment is welcomed. Thanks