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




  1. Is there a way to pass the Listen method a function that would accept KeyPressedEvent and thus removing the need for dynamic_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)

  2. Is the current system efficient as it is or is it a complete mess?

  3. 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)?










share|improve this question









New contributor




Gytautas is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
























    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




    1. Is there a way to pass the Listen method a function that would accept KeyPressedEvent and thus removing the need for dynamic_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)

    2. Is the current system efficient as it is or is it a complete mess?

    3. 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)?










    share|improve this question









    New contributor




    Gytautas is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
    Check out our Code of Conduct.






















      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




      1. Is there a way to pass the Listen method a function that would accept KeyPressedEvent and thus removing the need for dynamic_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)

      2. Is the current system efficient as it is or is it a complete mess?

      3. 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)?










      share|improve this question









      New contributor




      Gytautas is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.











      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




      1. Is there a way to pass the Listen method a function that would accept KeyPressedEvent and thus removing the need for dynamic_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)

      2. Is the current system efficient as it is or is it a complete mess?

      3. 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






      share|improve this question









      New contributor




      Gytautas is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.











      share|improve this question









      New contributor




      Gytautas is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      share|improve this question




      share|improve this question








      edited 6 mins ago









      Jamal

      30.2k11115226




      30.2k11115226






      New contributor




      Gytautas is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      asked 5 hours ago









      Gytautas

      111




      111




      New contributor




      Gytautas is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.





      New contributor





      Gytautas is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






      Gytautas is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.



























          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.










          draft saved

          draft discarded


















          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.










          draft saved

          draft discarded


















          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.




          draft saved


          draft discarded














          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





















































          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







          Popular posts from this blog

          Quarter-circle Tiles

          build a pushdown automaton that recognizes the reverse language of a given pushdown automaton?

          Mont Emei