Event system implementation with Listener and a static Manager
up vote
2
down vote
favorite
I am trying to create an event system that would be quite easy to use. The idea is that a user would only need to create a custom function and tell which event should fire that function. It works as is, but I was wondering if there is a more efficient way to accomplish this of which I'm not aware.
The main classes of the system
Event
EventListener
EventManager
EventType (size_t enum class)
Event
class Event
{
friend class EventListener;
public:
virtual EventType GetEventType() const = 0;
virtual ~Event() { }
inline bool IsHandled() const
{
return m_Handled;
}
protected:
bool m_Handled = false;
};
EventListener
using EventBehavior = std::function<bool(const Event& e)>;
class EventListener
{
public:
EventListener()
{
EventManager::AddListener(this);
}
template<class T>
void Listen(EventBehavior& behavior)
{
ASSERT(std::is_base_of<Event, T>, "Can't listen to non Event!");
m_ListeningTo[(size_t)T::GetStaticType()] = true;
m_RegisteredEvents[(size_t)T::GetStaticType()] = behavior;
}
template<class T>
void StopListening()
{
ASSERT(std::is_base_of<Event, T>, "Can't stop listening to non Event!");
m_ListeningTo[(size_t)T::GetStaticType()] = false;
}
void React(Event& event)
{
if (m_ListeningTo[(size_t)event.GetEventType()])
{
event.m_Handled = m_RegisteredEvents[(size_t)event.GetEventType()](event);
}
}
private:
std::bitset<MaxEvents> m_ListeningTo;
std::array<EventBehavior, MaxEvents> m_RegisteredEvents;
};
EventManager
class EventManager
{
public:
static void Post(Event* event)
{
m_EventBuffer.push_back(event);
}
static void Dispatch()
{
for (unsigned int i = 0; i < m_EventBuffer.size(); i++)
{
for (EventListener* listener : m_Listeners)
{
if (!m_EventBuffer[i]->IsHandled())
{
listener->React(*m_EventBuffer[i]);
}
else if(m_EventBuffer[i]->IsHandled())
{
delete m_EventBuffer[i];
m_EventBuffer.erase(m_EventBuffer.begin() + i);
break;
}
}
}
}
static void AddListener(EventListener* listener)
{
m_Listeners.push_back(listener);
}
static void ClearBuffer()
{
for (unsigned int i = 0; i < m_EventBuffer.size(); i++)
{
delete m_EventBuffer[i];
}
m_EventBuffer.clear();
}
private:
static std::vector<Event*> m_EventBuffer;
static std::vector<EventListener*> m_Listeners;
};
Example event
//Base class for all key events
class KeyEvent : public Event
{
public:
inline int GetKeyCode() const { return m_KeyCode; }
protected:
KeyEvent(int keycode)
: m_KeyCode(keycode), m_Mods(0) {}
KeyEvent(int keycode, int mods)
: m_KeyCode(keycode), m_Mods(mods) {}
int m_KeyCode;
int m_Mods;
};
class KeyPressedEvent : public KeyEvent
{
public:
KeyPressedEvent(int keycode, int repeatCount)
: KeyEvent(keycode, 0), m_RepeatCount(repeatCount) {}
KeyPressedEvent(int keycode, int repeatCount, int scancode, int mods)
: KeyEvent(keycode, mods), m_RepeatCount(repeatCount) {}
inline int GetRepeatCount() const { return m_RepeatCount; }
//EventType is a size_t enum class
static EventType GetStaticType() { return EventType::KeyPressed; }
virtual EventType GetEventType() const override { return GetStaticType(); }
private:
int m_RepeatCount;
};
The way the user uses the system
bool keyPressed(const Event& event)
{
const KeyPressedEvent& kpe = static_cast<const KeyPressedEvent&>(event);
//Do something
return true;
}
class Sandbox : private EventListener
{
public:
Sandbox()
{
this->Listen<KeyPressedEvent>(EventBehavior(keyPressed));
}
~Sandbox()
{
}
};
My main questions
- Is there a way to pass the
Listen
method a function that would acceptKeyPressedEvent
and thus removing the need fordynamic_cast
(I realize this will require a code change and I am glad to do that as long as the way the user would use the system would remain the same) - Is the current system efficient as it is or is it a complete mess?
- What would be some similar alternatives (I tried making the
EventBehavior
a template, but then ran into problems trying to store it inside the array)?
c++ event-handling
New contributor
add a comment |
up vote
2
down vote
favorite
I am trying to create an event system that would be quite easy to use. The idea is that a user would only need to create a custom function and tell which event should fire that function. It works as is, but I was wondering if there is a more efficient way to accomplish this of which I'm not aware.
The main classes of the system
Event
EventListener
EventManager
EventType (size_t enum class)
Event
class Event
{
friend class EventListener;
public:
virtual EventType GetEventType() const = 0;
virtual ~Event() { }
inline bool IsHandled() const
{
return m_Handled;
}
protected:
bool m_Handled = false;
};
EventListener
using EventBehavior = std::function<bool(const Event& e)>;
class EventListener
{
public:
EventListener()
{
EventManager::AddListener(this);
}
template<class T>
void Listen(EventBehavior& behavior)
{
ASSERT(std::is_base_of<Event, T>, "Can't listen to non Event!");
m_ListeningTo[(size_t)T::GetStaticType()] = true;
m_RegisteredEvents[(size_t)T::GetStaticType()] = behavior;
}
template<class T>
void StopListening()
{
ASSERT(std::is_base_of<Event, T>, "Can't stop listening to non Event!");
m_ListeningTo[(size_t)T::GetStaticType()] = false;
}
void React(Event& event)
{
if (m_ListeningTo[(size_t)event.GetEventType()])
{
event.m_Handled = m_RegisteredEvents[(size_t)event.GetEventType()](event);
}
}
private:
std::bitset<MaxEvents> m_ListeningTo;
std::array<EventBehavior, MaxEvents> m_RegisteredEvents;
};
EventManager
class EventManager
{
public:
static void Post(Event* event)
{
m_EventBuffer.push_back(event);
}
static void Dispatch()
{
for (unsigned int i = 0; i < m_EventBuffer.size(); i++)
{
for (EventListener* listener : m_Listeners)
{
if (!m_EventBuffer[i]->IsHandled())
{
listener->React(*m_EventBuffer[i]);
}
else if(m_EventBuffer[i]->IsHandled())
{
delete m_EventBuffer[i];
m_EventBuffer.erase(m_EventBuffer.begin() + i);
break;
}
}
}
}
static void AddListener(EventListener* listener)
{
m_Listeners.push_back(listener);
}
static void ClearBuffer()
{
for (unsigned int i = 0; i < m_EventBuffer.size(); i++)
{
delete m_EventBuffer[i];
}
m_EventBuffer.clear();
}
private:
static std::vector<Event*> m_EventBuffer;
static std::vector<EventListener*> m_Listeners;
};
Example event
//Base class for all key events
class KeyEvent : public Event
{
public:
inline int GetKeyCode() const { return m_KeyCode; }
protected:
KeyEvent(int keycode)
: m_KeyCode(keycode), m_Mods(0) {}
KeyEvent(int keycode, int mods)
: m_KeyCode(keycode), m_Mods(mods) {}
int m_KeyCode;
int m_Mods;
};
class KeyPressedEvent : public KeyEvent
{
public:
KeyPressedEvent(int keycode, int repeatCount)
: KeyEvent(keycode, 0), m_RepeatCount(repeatCount) {}
KeyPressedEvent(int keycode, int repeatCount, int scancode, int mods)
: KeyEvent(keycode, mods), m_RepeatCount(repeatCount) {}
inline int GetRepeatCount() const { return m_RepeatCount; }
//EventType is a size_t enum class
static EventType GetStaticType() { return EventType::KeyPressed; }
virtual EventType GetEventType() const override { return GetStaticType(); }
private:
int m_RepeatCount;
};
The way the user uses the system
bool keyPressed(const Event& event)
{
const KeyPressedEvent& kpe = static_cast<const KeyPressedEvent&>(event);
//Do something
return true;
}
class Sandbox : private EventListener
{
public:
Sandbox()
{
this->Listen<KeyPressedEvent>(EventBehavior(keyPressed));
}
~Sandbox()
{
}
};
My main questions
- Is there a way to pass the
Listen
method a function that would acceptKeyPressedEvent
and thus removing the need fordynamic_cast
(I realize this will require a code change and I am glad to do that as long as the way the user would use the system would remain the same) - Is the current system efficient as it is or is it a complete mess?
- What would be some similar alternatives (I tried making the
EventBehavior
a template, but then ran into problems trying to store it inside the array)?
c++ event-handling
New contributor
add a comment |
up vote
2
down vote
favorite
up vote
2
down vote
favorite
I am trying to create an event system that would be quite easy to use. The idea is that a user would only need to create a custom function and tell which event should fire that function. It works as is, but I was wondering if there is a more efficient way to accomplish this of which I'm not aware.
The main classes of the system
Event
EventListener
EventManager
EventType (size_t enum class)
Event
class Event
{
friend class EventListener;
public:
virtual EventType GetEventType() const = 0;
virtual ~Event() { }
inline bool IsHandled() const
{
return m_Handled;
}
protected:
bool m_Handled = false;
};
EventListener
using EventBehavior = std::function<bool(const Event& e)>;
class EventListener
{
public:
EventListener()
{
EventManager::AddListener(this);
}
template<class T>
void Listen(EventBehavior& behavior)
{
ASSERT(std::is_base_of<Event, T>, "Can't listen to non Event!");
m_ListeningTo[(size_t)T::GetStaticType()] = true;
m_RegisteredEvents[(size_t)T::GetStaticType()] = behavior;
}
template<class T>
void StopListening()
{
ASSERT(std::is_base_of<Event, T>, "Can't stop listening to non Event!");
m_ListeningTo[(size_t)T::GetStaticType()] = false;
}
void React(Event& event)
{
if (m_ListeningTo[(size_t)event.GetEventType()])
{
event.m_Handled = m_RegisteredEvents[(size_t)event.GetEventType()](event);
}
}
private:
std::bitset<MaxEvents> m_ListeningTo;
std::array<EventBehavior, MaxEvents> m_RegisteredEvents;
};
EventManager
class EventManager
{
public:
static void Post(Event* event)
{
m_EventBuffer.push_back(event);
}
static void Dispatch()
{
for (unsigned int i = 0; i < m_EventBuffer.size(); i++)
{
for (EventListener* listener : m_Listeners)
{
if (!m_EventBuffer[i]->IsHandled())
{
listener->React(*m_EventBuffer[i]);
}
else if(m_EventBuffer[i]->IsHandled())
{
delete m_EventBuffer[i];
m_EventBuffer.erase(m_EventBuffer.begin() + i);
break;
}
}
}
}
static void AddListener(EventListener* listener)
{
m_Listeners.push_back(listener);
}
static void ClearBuffer()
{
for (unsigned int i = 0; i < m_EventBuffer.size(); i++)
{
delete m_EventBuffer[i];
}
m_EventBuffer.clear();
}
private:
static std::vector<Event*> m_EventBuffer;
static std::vector<EventListener*> m_Listeners;
};
Example event
//Base class for all key events
class KeyEvent : public Event
{
public:
inline int GetKeyCode() const { return m_KeyCode; }
protected:
KeyEvent(int keycode)
: m_KeyCode(keycode), m_Mods(0) {}
KeyEvent(int keycode, int mods)
: m_KeyCode(keycode), m_Mods(mods) {}
int m_KeyCode;
int m_Mods;
};
class KeyPressedEvent : public KeyEvent
{
public:
KeyPressedEvent(int keycode, int repeatCount)
: KeyEvent(keycode, 0), m_RepeatCount(repeatCount) {}
KeyPressedEvent(int keycode, int repeatCount, int scancode, int mods)
: KeyEvent(keycode, mods), m_RepeatCount(repeatCount) {}
inline int GetRepeatCount() const { return m_RepeatCount; }
//EventType is a size_t enum class
static EventType GetStaticType() { return EventType::KeyPressed; }
virtual EventType GetEventType() const override { return GetStaticType(); }
private:
int m_RepeatCount;
};
The way the user uses the system
bool keyPressed(const Event& event)
{
const KeyPressedEvent& kpe = static_cast<const KeyPressedEvent&>(event);
//Do something
return true;
}
class Sandbox : private EventListener
{
public:
Sandbox()
{
this->Listen<KeyPressedEvent>(EventBehavior(keyPressed));
}
~Sandbox()
{
}
};
My main questions
- Is there a way to pass the
Listen
method a function that would acceptKeyPressedEvent
and thus removing the need fordynamic_cast
(I realize this will require a code change and I am glad to do that as long as the way the user would use the system would remain the same) - Is the current system efficient as it is or is it a complete mess?
- What would be some similar alternatives (I tried making the
EventBehavior
a template, but then ran into problems trying to store it inside the array)?
c++ event-handling
New contributor
I am trying to create an event system that would be quite easy to use. The idea is that a user would only need to create a custom function and tell which event should fire that function. It works as is, but I was wondering if there is a more efficient way to accomplish this of which I'm not aware.
The main classes of the system
Event
EventListener
EventManager
EventType (size_t enum class)
Event
class Event
{
friend class EventListener;
public:
virtual EventType GetEventType() const = 0;
virtual ~Event() { }
inline bool IsHandled() const
{
return m_Handled;
}
protected:
bool m_Handled = false;
};
EventListener
using EventBehavior = std::function<bool(const Event& e)>;
class EventListener
{
public:
EventListener()
{
EventManager::AddListener(this);
}
template<class T>
void Listen(EventBehavior& behavior)
{
ASSERT(std::is_base_of<Event, T>, "Can't listen to non Event!");
m_ListeningTo[(size_t)T::GetStaticType()] = true;
m_RegisteredEvents[(size_t)T::GetStaticType()] = behavior;
}
template<class T>
void StopListening()
{
ASSERT(std::is_base_of<Event, T>, "Can't stop listening to non Event!");
m_ListeningTo[(size_t)T::GetStaticType()] = false;
}
void React(Event& event)
{
if (m_ListeningTo[(size_t)event.GetEventType()])
{
event.m_Handled = m_RegisteredEvents[(size_t)event.GetEventType()](event);
}
}
private:
std::bitset<MaxEvents> m_ListeningTo;
std::array<EventBehavior, MaxEvents> m_RegisteredEvents;
};
EventManager
class EventManager
{
public:
static void Post(Event* event)
{
m_EventBuffer.push_back(event);
}
static void Dispatch()
{
for (unsigned int i = 0; i < m_EventBuffer.size(); i++)
{
for (EventListener* listener : m_Listeners)
{
if (!m_EventBuffer[i]->IsHandled())
{
listener->React(*m_EventBuffer[i]);
}
else if(m_EventBuffer[i]->IsHandled())
{
delete m_EventBuffer[i];
m_EventBuffer.erase(m_EventBuffer.begin() + i);
break;
}
}
}
}
static void AddListener(EventListener* listener)
{
m_Listeners.push_back(listener);
}
static void ClearBuffer()
{
for (unsigned int i = 0; i < m_EventBuffer.size(); i++)
{
delete m_EventBuffer[i];
}
m_EventBuffer.clear();
}
private:
static std::vector<Event*> m_EventBuffer;
static std::vector<EventListener*> m_Listeners;
};
Example event
//Base class for all key events
class KeyEvent : public Event
{
public:
inline int GetKeyCode() const { return m_KeyCode; }
protected:
KeyEvent(int keycode)
: m_KeyCode(keycode), m_Mods(0) {}
KeyEvent(int keycode, int mods)
: m_KeyCode(keycode), m_Mods(mods) {}
int m_KeyCode;
int m_Mods;
};
class KeyPressedEvent : public KeyEvent
{
public:
KeyPressedEvent(int keycode, int repeatCount)
: KeyEvent(keycode, 0), m_RepeatCount(repeatCount) {}
KeyPressedEvent(int keycode, int repeatCount, int scancode, int mods)
: KeyEvent(keycode, mods), m_RepeatCount(repeatCount) {}
inline int GetRepeatCount() const { return m_RepeatCount; }
//EventType is a size_t enum class
static EventType GetStaticType() { return EventType::KeyPressed; }
virtual EventType GetEventType() const override { return GetStaticType(); }
private:
int m_RepeatCount;
};
The way the user uses the system
bool keyPressed(const Event& event)
{
const KeyPressedEvent& kpe = static_cast<const KeyPressedEvent&>(event);
//Do something
return true;
}
class Sandbox : private EventListener
{
public:
Sandbox()
{
this->Listen<KeyPressedEvent>(EventBehavior(keyPressed));
}
~Sandbox()
{
}
};
My main questions
- Is there a way to pass the
Listen
method a function that would acceptKeyPressedEvent
and thus removing the need fordynamic_cast
(I realize this will require a code change and I am glad to do that as long as the way the user would use the system would remain the same) - Is the current system efficient as it is or is it a complete mess?
- What would be some similar alternatives (I tried making the
EventBehavior
a template, but then ran into problems trying to store it inside the array)?
c++ event-handling
c++ event-handling
New contributor
New contributor
edited 6 mins ago
Jamal♦
30.2k11115226
30.2k11115226
New contributor
asked 5 hours ago
Gytautas
111
111
New contributor
New contributor
add a comment |
add a comment |
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Gytautas is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f209784%2fevent-system-implementation-with-listener-and-a-static-manager%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
Gytautas is a new contributor. Be nice, and check out our Code of Conduct.
Gytautas is a new contributor. Be nice, and check out our Code of Conduct.
Gytautas is a new contributor. Be nice, and check out our Code of Conduct.
Gytautas is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f209784%2fevent-system-implementation-with-listener-and-a-static-manager%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown