utils: EventBus reimplementation 35/90335/7
authorLukasz Stanislawski <l.stanislaws@samsung.com>
Thu, 29 Sep 2016 09:06:41 +0000 (11:06 +0200)
committerLukasz Stanislawski <l.stanislaws@samsung.com>
Mon, 10 Oct 2016 08:43:28 +0000 (10:43 +0200)
Reimplement EventBus. Instead of Registration/Unregistration
methods a listener based implementation has been added.
Listeners now make automatic unregistration in the destructor.

The boilerplate code needed in defining of new event has
been significantly reduced.

Change-Id: Ifce4bd45bd60b3e8c9a9c99fe443794d8a47b380

16 files changed:
clock-tests/.cproject
clock-tests/src/AlarmPresenterTestCase.cpp
clock-tests/src/EventBusTest.cpp [new file with mode: 0644]
clock-tests/unittest.xml
clock/inc/Controller/MainController.h
clock/inc/Model/AlarmEvent.h
clock/inc/Model/AlarmProviderEvent.h
clock/inc/Presenter/AlarmPresenter.h
clock/inc/Utils/EventBus.h
clock/src/Controller/MainController.cpp
clock/src/Model/AlarmEvent.cpp [deleted file]
clock/src/Model/AlarmProviderEvent.cpp [deleted file]
clock/src/Model/AlarmProviderFile.cpp
clock/src/Presenter/AlarmPresenter.cpp
clock/src/Presenter/EditAlarmPresenter.cpp
clock/src/Utils/EventBus.cpp

index 9bc995d..c97df95 100644 (file)
                                                                        <listOptionValue builtIn="false" value="&quot;${SBI_SYSROOT}/usr/lib/glib-2.0/include&quot;"/>
                                                                </option>
                                                                <option id="sbi.gnu.c.compiler.option.frameworks_cflags.core.483995534" name="Tizen-Frameworks-Other-Cflags" superClass="sbi.gnu.c.compiler.option.frameworks_cflags.core" valueType="stringList">
-                                                                       <listOptionValue builtIn="false" value="${TC_COMPILER_MISC}"/>
-                                                                       <listOptionValue builtIn="false" value="${RS_COMPILER_MISC}"/>
+                                                                       <listOptionValue builtIn="false" value="$(TC_COMPILER_MISC)"/>
+                                                                       <listOptionValue builtIn="false" value="$(RS_COMPILER_MISC)"/>
                                                                        <listOptionValue builtIn="false" value=" -fPIE"/>
-                                                                       <listOptionValue builtIn="false" value="--sysroot=&quot;${SBI_SYSROOT}&quot;"/>
+                                                                       <listOptionValue builtIn="false" value="--sysroot=&quot;$(SBI_SYSROOT)&quot;"/>
                                                                        <listOptionValue builtIn="false" value="-mthumb"/>
                                                                </option>
                                                                <option id="gnu.c.compiler.option.include.paths.717856518" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" valueType="includePath">
                                                                <option defaultValue="false" id="sbi.gnu.cpp.linker.option.shared_flag.core.354840357" name="Linker.Shared" superClass="sbi.gnu.cpp.linker.option.shared_flag.core" valueType="boolean"/>
                                                                <option defaultValue="false" id="sbi.gnu.cpp.linker.option.noundefined.core.306613058" name="Report unresolved symbol references (-Wl,--no-undefined)" superClass="sbi.gnu.cpp.linker.option.noundefined.core" valueType="boolean"/>
                                                                <option id="sbi.gnu.cpp.linker.option.frameworks_lflags.core.839443319" name="Tizen-Frameworks-Other-Lflags" superClass="sbi.gnu.cpp.linker.option.frameworks_lflags.core" valueType="stringList">
-                                                                       <listOptionValue builtIn="false" value="${TC_LINKER_MISC}"/>
-                                                                       <listOptionValue builtIn="false" value="${RS_LINKER_MISC}"/>
+                                                                       <listOptionValue builtIn="false" value="$(TC_LINKER_MISC)"/>
+                                                                       <listOptionValue builtIn="false" value="$(RS_LINKER_MISC)"/>
                                                                        <listOptionValue builtIn="false" value="-pie -lpthread "/>
-                                                                       <listOptionValue builtIn="false" value="--sysroot=&quot;${SBI_SYSROOT}&quot;"/>
-                                                                       <listOptionValue builtIn="false" value="-Xlinker --version-script=&quot;${PROJ_PATH}/.exportMap&quot;"/>
-                                                                       <listOptionValue builtIn="false" value="-L&quot;${SBI_SYSROOT}/usr/lib&quot;"/>
+                                                                       <listOptionValue builtIn="false" value="--sysroot=&quot;$(SBI_SYSROOT)&quot;"/>
+                                                                       <listOptionValue builtIn="false" value="-Xlinker --version-script=&quot;$(PROJ_PATH)/.exportMap&quot;"/>
+                                                                       <listOptionValue builtIn="false" value="-L&quot;$(SBI_SYSROOT)/usr/lib&quot;"/>
                                                                        <listOptionValue builtIn="false" value="$(RS_LIBRARIES)"/>
+                                                                       <listOptionValue builtIn="false" value="-Xlinker -rpath=&quot;/opt/usr/apps/org.example.clocktest/lib&quot;"/>
                                                                </option>
                                                                <option id="gnu.cpp.link.option.paths.1558429204" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths" valueType="libPaths">
                                                                        <listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/lib}&quot;"/>
                                                                        <listOptionValue builtIn="false" value="&quot;/home/l.stanislaws/Projects/clock/clock/Debug/src/View/TimerView.o&quot;"/>
                                                                        <listOptionValue builtIn="false" value="&quot;/home/l.stanislaws/Projects/clock/clock/Debug/src/View/WorldClockView.o&quot;"/>
                                                                        <listOptionValue builtIn="false" value="&quot;/home/l.stanislaws/Projects/clock/clock/Debug/src/Controller/RingController.o&quot;"/>
-                                                                       <listOptionValue builtIn="false" value="&quot;/home/l.stanislaws/Projects/clock/clock/Debug/src/Model/AlarmEvent.o&quot;"/>
-                                                                       <listOptionValue builtIn="false" value="&quot;/home/l.stanislaws/Projects/clock/clock/Debug/src/Model/AlarmProviderEvent.o&quot;"/>
                                                                        <listOptionValue builtIn="false" value="&quot;/home/l.stanislaws/Projects/clock/clock/Debug/src/Model/Timer.o&quot;"/>
                                                                        <listOptionValue builtIn="false" value="&quot;/home/l.stanislaws/Projects/clock/clock/Debug/src/Model/WorldClock.o&quot;"/>
                                                                        <listOptionValue builtIn="false" value="&quot;/home/l.stanislaws/Projects/clock/clock/Debug/src/Presenter/DeleteAlarmPresenter.o&quot;"/>
index 18b6af5..dd43de9 100644 (file)
@@ -38,14 +38,5 @@ class TestSuite : public testing::Test
 // Otherwise, you define a test using TEST.
 TEST(TestSuite, test01)
 {
-       ASSERT_TRUE(true);
 }
 
-TEST(TestSuite, test02)
-{
-       MockTurtle turtle;
-       //presenter::AlarmPresenter *pres = new presenter::AlarmPresenter(nullptr, nullptr);
-
-       EXPECT_CALL(turtle, PenUp())
-               .Times(AtLeast(1));
-}
diff --git a/clock-tests/src/EventBusTest.cpp b/clock-tests/src/EventBusTest.cpp
new file mode 100644 (file)
index 0000000..6327ce0
--- /dev/null
@@ -0,0 +1,185 @@
+#include "Utils/EventBus.h"
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include <vector>
+#include "log.h"
+
+using namespace testing;
+using namespace utils;
+using std::placeholders::_1;
+
+class EventBusTestSuite : public testing::Test
+{
+       protected:
+       // virtual void SetUp() will be called before each test is run.
+       // You should define it if you need to initialize the variables.
+       // Otherwise, you don't have to provide it.
+       virtual void SetUp()
+       {
+
+       }
+       // virtual void TearDown() will be called after each test is run.
+       // You should define it if there is cleanup work to do.
+       // Otherwise, you don't have to provide it.
+       virtual void TearDown()
+       {
+
+       }
+};
+
+class Subscriber
+{
+       public:
+               Subscriber() : times_(0) {}
+               int times_;
+               void OnEvent(utils::Event *e);
+               void OnEvent2(utils::Event &e);
+};
+
+void Subscriber::OnEvent(utils::Event *event)
+{
+       times_++;
+}
+
+void Subscriber::OnEvent2(utils::Event &event)
+{
+       times_++;
+}
+
+class TestEvent : public utils::Event {
+};
+
+
+TEST(EventBusTestSuite, listener)
+{
+       Subscriber s1, s2;
+       TestEvent event;
+
+       Listener l1 = EventBus::AddListener<TestEvent>(std::bind(&Subscriber::OnEvent2, &s1, _1));
+       Listener l2 = EventBus::AddListener<TestEvent>(std::bind(&Subscriber::OnEvent2, &s2, _1));
+
+       EXPECT_EQ(s1.times_, 0);
+       EXPECT_EQ(s2.times_, 0);
+       EventBus::FireEvent(event);
+       EXPECT_EQ(s1.times_, 1);
+       EXPECT_EQ(s2.times_, 1);
+}
+
+TEST(EventBusTestSuite, listener2)
+{
+       Subscriber s1, s2;
+       TestEvent event;
+
+       Listener l1 = EventBus::AddListener<TestEvent>(std::bind(&Subscriber::OnEvent2, &s1, _1));
+       Listener l2 = EventBus::AddListener<TestEvent>(std::bind(&Subscriber::OnEvent2, &s2, _1));
+
+       EXPECT_EQ(s1.times_, 0);
+       EXPECT_EQ(s2.times_, 0);
+       EventBus::FireEvent(event);
+       EXPECT_EQ(s1.times_, 1);
+       EXPECT_EQ(s2.times_, 1);
+
+       l1.Unregister();
+       EventBus::FireEvent(event);
+       EXPECT_EQ(s1.times_, 1);
+       EXPECT_EQ(s2.times_, 2);
+
+       l2.Unregister();
+       EventBus::FireEvent(event);
+       EXPECT_EQ(s1.times_, 1);
+       EXPECT_EQ(s2.times_, 2);
+}
+
+TEST(EventBusTestSuite, listener_copy)
+{
+       Subscriber s1, s2;
+       TestEvent event;
+
+       Listener l1 = EventBus::AddListener<TestEvent>(std::bind(&Subscriber::OnEvent2, &s1, _1));
+       Listener l2 = EventBus::AddListener<TestEvent>(std::bind(&Subscriber::OnEvent2, &s2, _1));
+
+       EXPECT_EQ(s1.times_, 0);
+       EXPECT_EQ(s2.times_, 0);
+
+       EventBus::FireEvent(event);
+       EXPECT_EQ(s1.times_, 1);
+       EXPECT_EQ(s2.times_, 1);
+
+       Listener l3 = l1;
+       EventBus::FireEvent(event);
+       EXPECT_EQ(s1.times_, 3);
+       EXPECT_EQ(s2.times_, 2);
+
+       l1.Unregister();
+       EventBus::FireEvent(event);
+       EXPECT_EQ(s1.times_, 4);
+       EXPECT_EQ(s2.times_, 3);
+
+       l3.Unregister();
+       EventBus::FireEvent(event);
+       EXPECT_EQ(s1.times_, 4);
+       EXPECT_EQ(s2.times_, 4);
+
+       l2.Unregister();
+       EventBus::FireEvent(event);
+       EXPECT_EQ(s1.times_, 4);
+       EXPECT_EQ(s2.times_, 4);
+}
+
+TEST(EventBusTestSuite, listener_in_vector)
+{
+       DBG("Last test start");
+       Subscriber s1, s2;
+       TestEvent event;
+       std::vector<Listener> listeners;
+
+       listeners.push_back(EventBus::AddListener<TestEvent>(std::bind(&Subscriber::OnEvent2, &s1, _1)));
+       listeners.push_back(EventBus::AddListener<TestEvent>(std::bind(&Subscriber::OnEvent2, &s2, _1)));
+
+       EXPECT_EQ(s1.times_, 0);
+       EXPECT_EQ(s2.times_, 0);
+
+       EventBus::FireEvent(event);
+       EXPECT_EQ(s1.times_, 1);
+       EXPECT_EQ(s2.times_, 1);
+}
+
+TEST(EventBusTestSuite, listener_copy2)
+{
+       Subscriber s1, s2;
+       TestEvent event;
+
+       Listener l1 = EventBus::AddListener<TestEvent>(std::bind(&Subscriber::OnEvent2, &s1, _1));
+       Listener l2 = EventBus::AddListener<TestEvent>(std::bind(&Subscriber::OnEvent2, &s2, _1));
+
+       EXPECT_EQ(s1.times_, 0);
+       EXPECT_EQ(s2.times_, 0);
+
+       /* Unregister l1 subscription and make new subscription like l2 */
+       l1 = l2;
+
+       EventBus::FireEvent(event);
+       EXPECT_EQ(s1.times_, 0);
+       EXPECT_EQ(s2.times_, 2);
+}
+
+TEST(EventBusTestSuite, listener_copy3)
+{
+       Subscriber s1, s2;
+       Listener l3;
+       TestEvent event;
+
+       Listener l1 = EventBus::AddListener<TestEvent>(std::bind(&Subscriber::OnEvent2, &s1, _1));
+       Listener l2 = EventBus::AddListener<TestEvent>(std::bind(&Subscriber::OnEvent2, &s2, _1));
+
+       EXPECT_EQ(s1.times_, 0);
+       EXPECT_EQ(s2.times_, 0);
+
+       /* Unregister l1 subscription and make new subscription like empty subscription l3 */
+       l1 = l3;
+
+       EventBus::FireEvent(event);
+       EXPECT_EQ(s1.times_, 0);
+       EXPECT_EQ(s2.times_, 1);
+}
index 251bd54..95eea85 100644 (file)
@@ -24,8 +24,6 @@
 <targetObjectFile>/home/l.stanislaws/Projects/clock/clock/Debug/src/View/TimerView.o</targetObjectFile>
 <targetObjectFile>/home/l.stanislaws/Projects/clock/clock/Debug/src/View/WorldClockView.o</targetObjectFile>
 <targetObjectFile>/home/l.stanislaws/Projects/clock/clock/Debug/src/Controller/RingController.o</targetObjectFile>
-<targetObjectFile>/home/l.stanislaws/Projects/clock/clock/Debug/src/Model/AlarmEvent.o</targetObjectFile>
-<targetObjectFile>/home/l.stanislaws/Projects/clock/clock/Debug/src/Model/AlarmProviderEvent.o</targetObjectFile>
 <targetObjectFile>/home/l.stanislaws/Projects/clock/clock/Debug/src/Model/Timer.o</targetObjectFile>
 <targetObjectFile>/home/l.stanislaws/Projects/clock/clock/Debug/src/Model/WorldClock.o</targetObjectFile>
 <targetObjectFile>/home/l.stanislaws/Projects/clock/clock/Debug/src/Presenter/DeleteAlarmPresenter.o</targetObjectFile>
index 2ca57c4..524b23a 100644 (file)
 #ifndef _CLOCK_MAIN_CONTROLLER_H_
 #define _CLOCK_MAIN_CONTROLLER_H_
 
+#include <vector>
+
 #include "Utils/EventBus.h"
+#include "View/EditAlarmView.h"
+#include "Presenter/EditAlarmPresenter.h"
 
 #include "View/AlarmView.h"
 #include "View/StopWatchView.h"
@@ -107,12 +111,12 @@ namespace controller {
                        /**
                         * @brief Creates new "Edit/Create" alarm page.
                         */
-                       void CreateNewAlarmPage(utils::Event *e);
+                       void CreateNewAlarmPage(utils::Event &e);
 
                        /**
                         * @brief Creates new "Delete" alarm page.
                         */
-                       void CreateNewDeleteAlarmsPage(utils::Event *e);
+                       void CreateNewDeleteAlarmsPage(utils::Event &e);
 
                        model::WorldClock *world_clock_model_;
                        model::StopWatch *stop_watch_model_;
@@ -122,7 +126,19 @@ namespace controller {
                        presenter::WorldClockPresenter *world_clock_presenter_;
                        presenter::StopWatchPresenter *stop_watch_presenter_;
                        presenter::TimerPresenter *timer_presenter_;
+                       presenter::EditAlarmPresenter *edit_presenter_;
+
+                       view::EditAlarmView *edit_view_;
 
+                       /**
+                        * @brief Creates new "Edit" alarm page.
+                        */
+                       void CreateEditAlarmPage(utils::Event &e);
+
+                       /**
+                        * @brief global events subscriptions vector
+                        */
+                       std::vector<utils::Listener> listeners_;
        };
 }
 
index 113ddc0..58b827b 100644 (file)
 
 namespace model {
        class AlarmCreateRequestEvent : public utils::Event {
-               public:
-                       AlarmCreateRequestEvent();
-                       static int EventType();
-               private:
-                       static int custom_type_;
        };
        class AlarmDeleteRequestEvent : public utils::Event {
-               public:
-                       AlarmDeleteRequestEvent();
-                       static int EventType();
-               private:
-                       static int custom_type_;
        };
        class AlarmEditRequestEvent : public utils::Event {
                public:
-                       AlarmEditRequestEvent(model::Alarm& alarm);
-                       static int EventType();
+                       AlarmEditRequestEvent(model::Alarm& alarm) : alarm_(alarm) {}
                        Alarm &GetAlarm() const { return alarm_;}
                private:
                        Alarm &alarm_;
-                       static int custom_type_;
        };
        class AlarmEditedEvent : public utils::Event {
                public:
-                       AlarmEditedEvent(model::Alarm& alarm);
-                       static int EventType();
+                       AlarmEditedEvent(model::Alarm& alarm) : alarm_(alarm) {}
                        Alarm &GetAlarm() const { return alarm_;}
                private:
                        Alarm &alarm_;
-                       static int custom_type_;
        };
 } /* model */
 
index 5350566..b834c56 100644 (file)
 namespace model {
        class AlarmAddedEvent : public utils::Event {
                public:
-                       AlarmAddedEvent(Alarm &alarm);
+                       AlarmAddedEvent(Alarm &alarm) : alarm_(alarm) {}
                        Alarm& GetAlarm() { return alarm_; }
-                       static int EventType();
                private:
                        Alarm &alarm_;
-                       static int custom_type_;
        };
        class AlarmRemovedEvent : public utils::Event {
                public:
-                       AlarmRemovedEvent(Alarm &alarm);
+                       AlarmRemovedEvent(Alarm &alarm) : alarm_(alarm) {}
                        Alarm& GetAlarm() { return alarm_; }
-                       static int EventType();
                private:
                        Alarm &alarm_;
-                       static int custom_type_;
        };
 } /* model */
 
index 8cf2319..775007c 100644 (file)
 #include "View/AlarmView.h"
 #include "Model/AlarmProvider.h"
 #include "Model/AlarmProviderEvent.h"
+#include "Utils/EventBus.h"
 
 #include <map>
+#include <vector>
 
 namespace presenter {
        class AlarmPresenter {
@@ -35,12 +37,13 @@ namespace presenter {
                        void OnItemSelected(int idx);
                        void OnAddButtonClicked();
                        void OnItemActiveStatusChanged(int idx);
-                       void OnAlarmAddedEvent(utils::Event *e);
-                       void OnAlarmRemovedEvent(utils::Event *e);
-                       void OnAlarmEditedEvent(utils::Event *e);
+                       void OnAlarmAddedEvent(utils::Event &e);
+                       void OnAlarmRemovedEvent(utils::Event &e);
+                       void OnAlarmEditedEvent(utils::Event &e);
                        void OnDeleteItemClicked();
                        void UpdateBackgroundLabel();
                        std::map<int, std::reference_wrapper<model::Alarm>> alarms_;
+                       std::vector<utils::Listener> listeners_;
        };
 } /* presenters */
 
index 94ade98..289e19c 100644 (file)
 #include <functional>
 #include <vector>
 #include <map>
+#include <list>
+#include <typeindex>
 
 namespace utils {
+       /**
+        * @brief Base event class.
+        */
        class Event {
                public:
-                       Event(int type) : type_(type) {}
+                       Event() {}
                        virtual ~Event() {}
-                       int GetType() const { return type_; }
+       };
+
+       /**
+        * @brief Listener class represents event subscription.
+        *
+        * Listeners are created with EventBus::AddListener function.
+        */
+       class Listener {
+               public:
+                       /**
+                        * @brief default constructor
+                        *
+                        * Creates listener without any subscription.
+                        */
+                       Listener() : owner_(nullptr) {}
+
+                       /**
+                        * @brief Copy constructor. Creates new event subscription.
+                        */
+                       Listener(const Listener&);
+
+                       /**
+                        * @brief Stealing constructor
+                        */
+                       Listener(Listener&& obj);
+
+                       /**
+                        * @brief Destructor
+                        *
+                        * Destructor Unregisters event subscription.
+                        */
+                       ~Listener();
+
+                       /**
+                        * @brief Unregister current event subscription.
+                        */
+                       void Unregister();
+
+                       /**
+                        * @brief Assignment operator.
+                        *
+                        * Assignment make two thins:
+                        * 1. Unregisters current event subscription.
+                        * 2. Creates new subscription identical to right value.
+                        *
+                        * example:
+                        * Listener l1 = EventBus::AllListner(std::bind(&MyClass::MyMethod,
+                        * obj1, _1));
+                        * Listener l2 = EventBus::AllListner(std::bind(&MyClass::MyMethod,
+                        * obj2, _1));
+                        *
+                        * // subscription on obj1 get unregistered, new registration on obj2
+                        * // has been set in l1.
+                        *
+                        * l1 = l2;
+                        *
+                        * EventBus::FireEvent(...) // MyMethod on obj2 will be called 2 times.
+                        */
+                       Listener& operator=(const Listener&);
+
+                       /**
+                        * @brief Constructor used to create listener subscribed to event.
+                        */
+                       Listener(std::list<std::function<void(Event&)>> &owner,
+                               std::list<std::function<void(Event&)>>::iterator it);
+
                private:
-                       int type_;
+                       std::list<std::function<void(Event&)>> *owner_;
+                       std::list<std::function<void(Event&)>>::iterator it_;
        };
+
        /**
-        * @brief EventBus class is not thread safe. Please avoid using EventBus in
-        * multithreaded applications.
+        * @brief Global events dispatcher.
         *
         * How to use Event bus:
-        *
-        * 1. Create new Event type class with static type identifier.
-        * 2. Define static variable identyfying type of your class
-        * 3. Define static function to get event type of your class
+        * 1. Create new Event type class
+        * 2. Register listener by invoking AddListener function
         *
         * Example:
         *
         * class DerivedEvent : public Event {
-        *    public:
-        *        DerivedEvent() : Event(EventType()) {}
-        *    private:
-        *        static int custom_type_;
-        *        static int EventType() {
-        *            if (custom_type_ == 0) {
-        *                custom_type = EventBus::RegisterNewEventType();
-        *            }
-        *            return custom_type;
-        *        }
-        * }
+        * };
+        *
+        * how to register on event:
+        * EventBus::AddListener<DerivedEvent>(std_function_object);
         *
-        * how to register on evnet:
-        * EventBus::RegisterHandler(DerivedEvent::EventType(), some_callback)
+        * how to fire event:
+        * DerivedEvent ev;
+        * EventBus::FireEvent(ev);
         *
-        * how to fire event
-        * EventBus::FireEvent(new DerivedEvent());
+        * @note EventBus class is not thread safe. Please avoid using EventBus in
+        * multithreaded applications.
         *
         */
        class EventBus {
                public:
                        /**
-                        * @brief Registers new data type
-                        * @return new event type descriptor (> 1)
-                        */
-                       static int RegisterNewEventType();
-
-                       /**
                         * @brief Registers handler for given event type
                         */
-                       static void RegisterHandler(int type, std::function<void(Event*)>);
-
-                       /**
-                        * @brief Deregisters handler for given event type
-                        */
-                       static void DeregisterHandler(int type, std::function<void(Event*)>);
+                       template <typename T>
+                       static Listener AddListener(std::function<void(Event&)> func)
+                       {
+                               return AddListenerInternal(typeid(T), func);
+                       }
 
                        /**
-                        * @brief Fire event. This means running all handlers registered
-                        * with @RegisterHandler function.
-                        *
-                        * @param must be allocated with 'new' operator
+                        * @brief Fire event. This means running all listener registered
+                        * with @AddListener function.
                         */
-                       static void FireEvent(Event*);
+                       static void FireEvent(Event&);
 
                        /**
-                        * @brief Deleted copy constructor
+                        * @brief Deleted copy constructor.
                         */
                        EventBus(const EventBus &bus) = delete;
 
                        /**
-                        * @brief Deleted assignment operator
+                        * @brief Deleted assignment operator.
                         */
                        void operator=(const EventBus &bus) = delete;
+
                private:
+                       static Listener AddListenerInternal(const std::type_index,
+                                       std::function<void(Event&)>);
                        static EventBus& GetInstance();
-                       EventBus();
-                       int current_event_id_;
-                       std::map<int, std::vector<std::function<void(Event*)>>> callbacks_;
+                       EventBus() {}
+                       std::map<std::type_index, std::list<std::function<void(Event&)>>> callbacks_;
        };
 } /* utils */
 
index 8f284ac..a4fb72d 100644 (file)
@@ -65,13 +65,12 @@ int MainController::Init()
        stop_watch_presenter_ = new StopWatchPresenter((StopWatchView *)MainView::GetInstance().GetView(STOP_WATCH), stop_watch_model_);
        timer_presenter_ = new TimerPresenter((TimerView *)MainView::GetInstance().GetView(TIMER), timer_model_);
 
-
-       EventBus::RegisterHandler(AlarmCreateRequestEvent::EventType(),
-                       std::bind(&MainController::CreateNewAlarmPage, this, _1));
-       EventBus::RegisterHandler(AlarmDeleteRequestEvent::EventType(),
-                       std::bind(&MainController::CreateNewDeleteAlarmsPage, this, _1));
-       EventBus::RegisterHandler(AlarmEditRequestEvent::EventType(),
-                       std::bind(&MainController::CreateNewAlarmPage, this, _1));
+       listeners_.push_back(utils::EventBus::AddListener<AlarmCreateRequestEvent>(
+                       std::bind(&MainController::CreateNewAlarmPage, this, _1)));
+       listeners_.push_back(utils::EventBus::AddListener<AlarmDeleteRequestEvent>(
+                       std::bind(&MainController::CreateNewDeleteAlarmsPage, this, _1)));
+       listeners_.push_back(utils::EventBus::AddListener<AlarmEditRequestEvent>(
+                       std::bind(&MainController::CreateEditAlarmPage, this, _1)));
 
        return 0;
 }
@@ -89,38 +88,30 @@ void MainController::Deinit()
        delete timer_presenter_;
        delete timer_model_;
 
-       EventBus::DeregisterHandler(AlarmCreateRequestEvent::EventType(),
-                       std::bind(&MainController::CreateNewAlarmPage, this, _1));
-       EventBus::DeregisterHandler(AlarmDeleteRequestEvent::EventType(),
-                       std::bind(&MainController::CreateNewDeleteAlarmsPage, this, _1));
-       EventBus::DeregisterHandler(AlarmEditRequestEvent::EventType(),
-                       std::bind(&MainController::CreateNewAlarmPage, this, _1));
+       listeners_.clear();
 }
 
-void MainController::CreateNewAlarmPage(Event *e)
+void MainController::CreateNewAlarmPage(Event &e)
 {
-       static EditAlarmView *view;
-       static EditAlarmPresenter *presenter;
-       Alarm *alarm;
-
-       if (e->GetType() == AlarmCreateRequestEvent::EventType()) {
-               alarm = nullptr;
-       } else if (e->GetType() == AlarmEditRequestEvent::EventType()) {
-               AlarmEditRequestEvent *ev = dynamic_cast<AlarmEditRequestEvent*>(e);
-               alarm = &ev->GetAlarm();
-       } else {
-               ERR("Unhandled event type");
-               return;
-       }
+       delete edit_view_;
+       delete edit_presenter_;
 
-       if (view) delete view;
-       if (presenter) delete presenter;
+       edit_view_ = new EditAlarmView();
+       edit_presenter_ = new EditAlarmPresenter(nullptr, *edit_view_);
+}
+
+void MainController::CreateEditAlarmPage(Event &e)
+{
+       AlarmEditRequestEvent &ev = (AlarmEditRequestEvent&)e;
+
+       delete edit_view_;
+       delete edit_presenter_;
 
-       view = new EditAlarmView();
-       presenter = new EditAlarmPresenter(alarm, *view);
+       edit_view_ = new EditAlarmView();
+       edit_presenter_ = new EditAlarmPresenter(&ev.GetAlarm(), *edit_view_);
 }
 
-void MainController::CreateNewDeleteAlarmsPage(Event *e)
+void MainController::CreateNewDeleteAlarmsPage(Event &e)
 {
        static DeleteAlarmView *view;
        static DeleteAlarmPresenter *presenter;
diff --git a/clock/src/Model/AlarmEvent.cpp b/clock/src/Model/AlarmEvent.cpp
deleted file mode 100644 (file)
index e7bdbdc..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#include "Model/AlarmEvent.h"
-
-using namespace model;
-using namespace utils;
-
-int AlarmCreateRequestEvent::custom_type_;
-int AlarmDeleteRequestEvent::custom_type_;
-int AlarmEditRequestEvent::custom_type_;
-int AlarmEditedEvent::custom_type_;
-
-AlarmCreateRequestEvent::AlarmCreateRequestEvent() :
-       Event(AlarmCreateRequestEvent::EventType())
-{
-}
-
-int AlarmCreateRequestEvent::EventType()
-{
-       if (custom_type_ == 0) {
-               custom_type_ = EventBus::RegisterNewEventType();
-       }
-       return custom_type_;
-}
-
-AlarmDeleteRequestEvent::AlarmDeleteRequestEvent() :
-       Event(AlarmDeleteRequestEvent::EventType())
-{
-}
-
-int AlarmDeleteRequestEvent::EventType()
-{
-       if (custom_type_ == 0) {
-               custom_type_ = EventBus::RegisterNewEventType();
-       }
-       return custom_type_;
-}
-
-AlarmEditRequestEvent::AlarmEditRequestEvent(Alarm &alarm) :
-       Event(AlarmEditRequestEvent::EventType()),
-       alarm_(alarm)
-{
-}
-
-int AlarmEditRequestEvent::EventType()
-{
-       if (custom_type_ == 0) {
-               custom_type_ = EventBus::RegisterNewEventType();
-       }
-       return custom_type_;
-}
-
-AlarmEditedEvent::AlarmEditedEvent(Alarm &alarm) :
-       Event(AlarmEditedEvent::EventType()),
-       alarm_(alarm)
-{
-}
-
-int AlarmEditedEvent::EventType()
-{
-       if (custom_type_ == 0) {
-               custom_type_ = EventBus::RegisterNewEventType();
-       }
-       return custom_type_;
-}
diff --git a/clock/src/Model/AlarmProviderEvent.cpp b/clock/src/Model/AlarmProviderEvent.cpp
deleted file mode 100644 (file)
index 99cb2ae..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#include "Model/AlarmProviderEvent.h"
-
-using namespace model;
-using namespace utils;
-
-int AlarmAddedEvent::custom_type_;
-int AlarmRemovedEvent::custom_type_;
-
-AlarmAddedEvent::AlarmAddedEvent(Alarm &alarm) :
-       Event(AlarmAddedEvent::EventType()), alarm_(alarm)
-{
-}
-
-int AlarmAddedEvent::EventType()
-{
-       if (custom_type_ == 0) {
-               custom_type_ = EventBus::RegisterNewEventType();
-       }
-       return custom_type_;
-}
-
-AlarmRemovedEvent::AlarmRemovedEvent(Alarm &alarm) :
-       Event(AlarmRemovedEvent::EventType()), alarm_(alarm)
-{
-}
-
-int AlarmRemovedEvent::EventType()
-{
-       if (custom_type_ == 0) {
-               custom_type_ = EventBus::RegisterNewEventType();
-       }
-       return custom_type_;
-}
index d7fdb46..9c1f988 100644 (file)
@@ -20,7 +20,8 @@ void AlarmProviderFile::Add(model::Alarm& alarm)
 
        if (it == alarms.end()) {
                alarms.push_back(alarm);
-               EventBus::FireEvent(new AlarmAddedEvent(alarms.back()));
+               AlarmAddedEvent ev(alarms.back());
+               EventBus::FireEvent(ev);
        }
        Sync();
 }
@@ -30,7 +31,8 @@ void AlarmProviderFile::Remove(std::reference_wrapper<Alarm> alarm)
        auto it = std::find(alarms.begin(), alarms.end(), alarm.get());
 
        if (it != alarms.end()) {
-               EventBus::FireEvent(new AlarmRemovedEvent(alarm));
+               AlarmRemovedEvent ev(alarm);
+               EventBus::FireEvent(ev);
                alarm.get().Deactivate();
                alarms.erase(it);
        }
index f7631d1..db1df4f 100644 (file)
@@ -17,24 +17,18 @@ AlarmPresenter::AlarmPresenter(AlarmView *v, AlarmProvider *m) : view(v), model(
        view->SetItemToggleCallback(std::bind(&AlarmPresenter::OnItemActiveStatusChanged, this, _1));
        view->SetItemSelectCallback(std::bind(&AlarmPresenter::OnItemSelected, this, _1));
 
-       EventBus::RegisterHandler(AlarmAddedEvent::EventType(),
-                       std::bind(&AlarmPresenter::OnAlarmAddedEvent, this, _1));
-       EventBus::RegisterHandler(AlarmRemovedEvent::EventType(),
-                       std::bind(&AlarmPresenter::OnAlarmRemovedEvent, this, _1));
-       EventBus::RegisterHandler(AlarmEditedEvent::EventType(),
-                       std::bind(&AlarmPresenter::OnAlarmEditedEvent, this, _1));
+       listeners_.push_back(EventBus::AddListener<AlarmAddedEvent>(
+                       std::bind(&AlarmPresenter::OnAlarmAddedEvent, this, _1)));
+       listeners_.push_back(EventBus::AddListener<AlarmRemovedEvent>(
+                       std::bind(&AlarmPresenter::OnAlarmRemovedEvent, this, _1)));
+       listeners_.push_back(EventBus::AddListener<AlarmEditedEvent>(
+                       std::bind(&AlarmPresenter::OnAlarmEditedEvent, this, _1)));
 
        ShowAll();
 }
 
 AlarmPresenter::~AlarmPresenter()
 {
-       EventBus::DeregisterHandler(AlarmAddedEvent::EventType(),
-                       std::bind(&AlarmPresenter::OnAlarmAddedEvent, this, _1));
-       EventBus::DeregisterHandler(AlarmRemovedEvent::EventType(),
-                       std::bind(&AlarmPresenter::OnAlarmRemovedEvent, this, _1));
-       EventBus::DeregisterHandler(AlarmEditedEvent::EventType(),
-                       std::bind(&AlarmPresenter::OnAlarmEditedEvent, this, _1));
 }
 
 void AlarmPresenter::ShowAll()
@@ -62,13 +56,15 @@ void AlarmPresenter::OnItemSelected(int idx)
                return;
 
        Alarm &alarm = it->second.get();
+       AlarmEditRequestEvent ev(alarm);
 
-       EventBus::FireEvent(new AlarmEditRequestEvent(alarm));
+       EventBus::FireEvent(ev);
 }
 
 void AlarmPresenter::OnAddButtonClicked()
 {
-       EventBus::FireEvent(new AlarmCreateRequestEvent());
+       AlarmCreateRequestEvent ev;
+       EventBus::FireEvent(ev);
 }
 
 void AlarmPresenter::OnItemActiveStatusChanged(int idx)
@@ -92,36 +88,32 @@ void AlarmPresenter::OnItemActiveStatusChanged(int idx)
        AlarmProvider::Sync();
 }
 
-void AlarmPresenter::OnAlarmAddedEvent(Event *e)
+void AlarmPresenter::OnAlarmAddedEvent(Event &e)
 {
-       if (!e || (e->GetType() != AlarmAddedEvent::EventType()))
-               return;
-
-       AlarmAddedEvent *ev = dynamic_cast<AlarmAddedEvent*>(e);
+       AlarmAddedEvent &ev = dynamic_cast<AlarmAddedEvent&>(e);
 
        int id = view->ItemAppend(
-                       ev->GetAlarm().GetTime(),
-                       ev->GetAlarm().GetName().c_str(),
-                       ev->GetAlarm().GetWeekFlags(),
-                       ev->GetAlarm().IsActivated());
-       alarms_.insert(std::map<int, std::reference_wrapper<Alarm>>::value_type (id, std::reference_wrapper<Alarm>(ev->GetAlarm())));
+                       ev.GetAlarm().GetTime(),
+                       ev.GetAlarm().GetName().c_str(),
+                       ev.GetAlarm().GetWeekFlags(),
+                       ev.GetAlarm().IsActivated());
+       alarms_.insert(std::map<int, std::reference_wrapper<Alarm>>::value_type (id, std::reference_wrapper<Alarm>(ev.GetAlarm())));
 
        UpdateBackgroundLabel();
 }
 
 void AlarmPresenter::OnDeleteItemClicked()
 {
-       EventBus::FireEvent(new AlarmDeleteRequestEvent());
+       AlarmDeleteRequestEvent ev;
+       EventBus::FireEvent(ev);
 }
 
-void AlarmPresenter::OnAlarmRemovedEvent(Event *e)
+void AlarmPresenter::OnAlarmRemovedEvent(Event &e)
 {
-       if (!e || (e->GetType() != AlarmRemovedEvent::EventType()))
-               return;
+       AlarmRemovedEvent &ev = dynamic_cast<AlarmRemovedEvent&>(e);
 
-       AlarmRemovedEvent *ev = dynamic_cast<AlarmRemovedEvent*>(e);
        for (auto it = alarms_.begin(); it != alarms_.end(); ++it) {
-               if (ev->GetAlarm() == it->second.get()) {
+               if (ev.GetAlarm() == it->second.get()) {
                        view->RemoveItem(it->first);
                        alarms_.erase(it);
                        break;
@@ -138,12 +130,13 @@ void AlarmPresenter::UpdateBackgroundLabel()
                view->HideNoAlarmsBackgroundLabel();
 }
 
-void AlarmPresenter::OnAlarmEditedEvent(utils::Event *e)
+void AlarmPresenter::OnAlarmEditedEvent(utils::Event &e)
 {
-       AlarmEditedEvent *ev = dynamic_cast<AlarmEditedEvent*>(e);
+       AlarmEditedEvent &ev = dynamic_cast<AlarmEditedEvent&>(e);
+
        auto it = alarms_.begin();
        for (; it != alarms_.end(); ++it) {
-               if (ev->GetAlarm() == it->second.get()) {
+               if (ev.GetAlarm() == it->second.get()) {
                        Alarm &alarm = it->second.get();
                        view->ItemUpdate(it->first, alarm.GetTime(), alarm.GetName().c_str(),
                                        alarm.GetWeekFlags(), alarm.IsActivated());
index 2133c68..1968eea 100644 (file)
@@ -82,7 +82,8 @@ void EditAlarmPresenter::OnEditDone()
                alarm->Activate();
                AlarmProvider::GetInstance()->Add(*alarm);
        } else {
-               EventBus::FireEvent(new AlarmEditedEvent(*alarm_));
+               AlarmEditedEvent ev(*alarm_);
+               EventBus::FireEvent(ev);
        }
 
        // delete local copy if needed
index 34c3a86..54dd456 100644 (file)
@@ -5,53 +5,76 @@
 
 using namespace utils;
 
-int EventBus::RegisterNewEventType()
+EventBus& EventBus::GetInstance()
 {
-       EventBus &bus = GetInstance();
-       return ++bus.current_event_id_;
+       static EventBus bus;
+       return bus;
 }
 
-void EventBus::RegisterHandler(int type, std::function<void(Event*)> func)
+void EventBus::FireEvent(Event& event)
 {
        EventBus &bus = GetInstance();
-       bus.callbacks_[type].push_back(func);
+
+       auto it = bus.callbacks_[typeid(event)].begin();
+       auto end = bus.callbacks_[typeid(event)].end();
+
+       for (; it != end; ++it) {
+               (*it)(event);
+       }
 }
 
-void EventBus::DeregisterHandler(int type, std::function<void(Event*)> func)
+Listener EventBus::AddListenerInternal(const std::type_index type, std::function<void(Event&)> cb)
 {
        EventBus &bus = GetInstance();
-       auto it = bus.callbacks_[type].begin();
-
-       // std::function do not have default comprarison operator, so compare
-       // them by target type's name and by pointer to stored type
-       for (it = bus.callbacks_[type].begin(); it != bus.callbacks_[type].end(); ++it) {
-               if (it->target_type().name() == func.target_type().name() &&
-                       it->target<void(Event*)>() == func.target<void(Event*)>()) {
-                       break;
-               }
-       }
+       bus.callbacks_[type].push_back(cb);
+       return Listener(bus.callbacks_[type], --bus.callbacks_[type].end());
+}
 
-       if (it != bus.callbacks_[type].end()) {
-               bus.callbacks_[type].erase(it);
-       }
+Listener::Listener(std::list<std::function<void(Event&)>> &owner,
+       std::list<std::function<void(Event&)>>::iterator it)
+{
+       owner_ = std::addressof(owner);
+       it_ = it;
 }
 
-void EventBus::FireEvent(Event *event)
+Listener::~Listener()
 {
-       EventBus &bus = GetInstance();
-       DBG("Fire %d event", event->GetType());
-       for (auto func: bus.callbacks_[event->GetType()]) {
-               func(event);
+       Unregister();
+}
+
+void Listener::Unregister()
+{
+       if (owner_) {
+               owner_->erase(it_);
+               owner_ = nullptr;
        }
-       delete event;
 }
 
-EventBus& EventBus::GetInstance()
+Listener::Listener(Listener&& ref)
 {
-       static EventBus bus;
-       return bus;
+       owner_ = ref.owner_;
+       it_ = std::move(ref.it_);
+       ref.owner_ = nullptr;
 }
 
-EventBus::EventBus()
+Listener::Listener(const Listener& ln)
 {
+       owner_ = ln.owner_;
+       if (owner_) {
+               owner_->push_back(*ln.it_);
+               it_ = --owner_->end();
+       }
+}
+
+Listener& Listener::operator=(const Listener& ln)
+{
+       // unregister first, as subscription may be set.
+       Unregister();
+
+       owner_ = ln.owner_;
+       if (owner_) {
+               owner_->push_back(*ln.it_);
+               it_ = --owner_->end();
+       }
+       return *this;
 }