util: Add EventEmitter

Change-Id: I08e8beea7a05abe30cc818ec2d44acffecf07343
diff --git a/src/util/event-emitter.hpp b/src/util/event-emitter.hpp
new file mode 100644
index 0000000..adbd231
--- /dev/null
+++ b/src/util/event-emitter.hpp
@@ -0,0 +1,381 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2014 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ * This code is actually copied from NFD project (NDN Forwarding Daemon).
+ * We acknowledge the permission of the authors of NFD.
+ */
+/**
+ * Copyright (c) 2014  Regents of the University of California,
+ *                     Arizona Board of Regents,
+ *                     Colorado State University,
+ *                     University Pierre & Marie Curie, Sorbonne University,
+ *                     Washington University in St. Louis,
+ *                     Beijing Institute of Technology
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#ifndef NDN_UTIL_EVENT_EMITTER_HPP
+#define NDN_UTIL_EVENT_EMITTER_HPP
+
+#include "common.hpp"
+#include <vector>
+
+namespace ndn {
+namespace util {
+
+struct empty
+{
+};
+
+/** \class EventEmitter
+ *  \brief provides a lightweight event system
+ *
+ *  To declare an event:
+ *    EventEmitter<TArgs> onEventName;
+ *  To subscribe to an event:
+ *    eventSource->onEventName += eventHandler;
+ *    Multiple functions can subscribe to the same event.
+ *  To trigger an event:
+ *    onEventName(args);
+ *  To clear event subscriptions:
+ *    onEventName.clear();
+ */
+
+// four arguments
+template<typename T1 = empty, typename T2 = empty,
+         typename T3 = empty, typename T4 = empty>
+class EventEmitter : noncopyable
+{
+public:
+  /// represents a handler that can subscribe to the event
+  typedef function<void(const T1&, const T2&,
+                        const T3&, const T4&)> Handler;
+
+  /// adds an subscription
+  void
+  operator+=(Handler handler);
+
+  /// returns true if there is no subscription,
+  /// otherwise returns false
+  bool
+  isEmpty() const;
+
+  /// clears all subscriptions
+  void
+  clear();
+
+  /// triggers the event
+  void
+  operator()(const T1& a1, const T2& a2, const T3& a3, const T4& a4);
+
+private:
+  /// stores all subscribed handlers
+  std::vector<Handler> m_handlers;
+};
+
+// zero argument
+template<>
+class EventEmitter<empty, empty, empty, empty> : noncopyable
+{
+public:
+  typedef function<void()> Handler;
+
+  void
+  operator+=(Handler handler);
+
+  bool
+  isEmpty() const;
+
+  void
+  clear();
+
+  void
+  operator()();
+
+private:
+  std::vector<Handler> m_handlers;
+};
+
+
+// one argument
+template<typename T1>
+class EventEmitter<T1, empty, empty, empty> : noncopyable
+{
+public:
+  typedef function<void(const T1&)> Handler;
+
+  void
+  operator+=(Handler handler);
+
+  bool
+  isEmpty() const;
+
+  void
+  clear();
+
+  void
+  operator()(const T1& a1);
+
+private:
+  std::vector<Handler> m_handlers;
+};
+
+
+// two arguments
+template<typename T1, typename T2>
+class EventEmitter<T1, T2, empty, empty> : noncopyable
+{
+public:
+  typedef function<void(const T1&, const T2&)> Handler;
+
+  void
+  operator+=(Handler handler);
+
+  bool
+  isEmpty() const;
+
+  void
+  clear();
+
+  void
+  operator()(const T1& a1, const T2& a2);
+
+private:
+  std::vector<Handler> m_handlers;
+};
+
+
+// three arguments
+template<typename T1, typename T2, typename T3>
+class EventEmitter<T1, T2, T3, empty> : noncopyable
+{
+public:
+  typedef function<void(const T1&, const T2&, const T3&)> Handler;
+
+  void
+  operator+=(Handler handler);
+
+  bool
+  isEmpty() const;
+
+  void
+  clear();
+
+  void
+  operator()(const T1& a1, const T2& a2, const T3& a3);
+
+private:
+  std::vector<Handler> m_handlers;
+};
+
+
+// zero argument
+
+inline void
+EventEmitter<empty, empty, empty, empty>::operator+=(Handler handler)
+{
+  m_handlers.push_back(handler);
+}
+
+inline bool
+EventEmitter<empty, empty, empty, empty>::isEmpty() const
+{
+  return m_handlers.empty();
+}
+
+inline void
+EventEmitter<empty, empty, empty, empty>::clear()
+{
+  return m_handlers.clear();
+}
+
+inline void
+EventEmitter<empty, empty, empty, empty>::operator()()
+{
+  std::vector<Handler>::iterator it;
+  for (it = m_handlers.begin(); it != m_handlers.end(); ++it) {
+    (*it)();
+    if (m_handlers.empty()) // .clear has been called
+      return;
+  }
+}
+
+// one argument
+
+template<typename T1>
+inline void
+EventEmitter<T1, empty, empty, empty>::operator+=(Handler handler)
+{
+  m_handlers.push_back(handler);
+}
+
+template<typename T1>
+inline bool
+EventEmitter<T1, empty, empty, empty>::isEmpty() const
+{
+  return m_handlers.empty();
+}
+
+template<typename T1>
+inline void
+EventEmitter<T1, empty, empty, empty>::clear()
+{
+  return m_handlers.clear();
+}
+
+template<typename T1>
+inline void
+EventEmitter<T1, empty, empty, empty>::operator()(const T1& a1)
+{
+  typename std::vector<Handler>::iterator it;
+  for (it = m_handlers.begin(); it != m_handlers.end(); ++it) {
+    (*it)(a1);
+    if (m_handlers.empty()) // .clear has been called
+      return;
+  }
+}
+
+// two arguments
+
+template<typename T1, typename T2>
+inline void
+EventEmitter<T1, T2, empty, empty>::operator+=(Handler handler)
+{
+  m_handlers.push_back(handler);
+}
+
+template<typename T1, typename T2>
+inline bool
+EventEmitter<T1, T2, empty, empty>::isEmpty() const
+{
+  return m_handlers.empty();
+}
+
+template<typename T1, typename T2>
+inline void
+EventEmitter<T1, T2, empty, empty>::clear()
+{
+  return m_handlers.clear();
+}
+
+template<typename T1, typename T2>
+inline void
+EventEmitter<T1, T2, empty, empty>::operator()
+    (const T1& a1, const T2& a2)
+{
+  typename std::vector<Handler>::iterator it;
+  for (it = m_handlers.begin(); it != m_handlers.end(); ++it) {
+    (*it)(a1, a2);
+    if (m_handlers.empty()) // .clear has been called
+      return;
+  }
+}
+
+// three arguments
+
+template<typename T1, typename T2, typename T3>
+inline void
+EventEmitter<T1, T2, T3, empty>::operator+=(Handler handler)
+{
+  m_handlers.push_back(handler);
+}
+
+template<typename T1, typename T2, typename T3>
+inline bool
+EventEmitter<T1, T2, T3, empty>::isEmpty() const
+{
+  return m_handlers.empty();
+}
+
+template<typename T1, typename T2, typename T3>
+inline void
+EventEmitter<T1, T2, T3, empty>::clear()
+{
+  return m_handlers.clear();
+}
+
+template<typename T1, typename T2, typename T3>
+inline void
+EventEmitter<T1, T2, T3, empty>::operator()
+    (const T1& a1, const T2& a2, const T3& a3)
+{
+  typename std::vector<Handler>::iterator it;
+  for (it = m_handlers.begin(); it != m_handlers.end(); ++it) {
+    (*it)(a1, a2, a3);
+    if (m_handlers.empty()) // .clear has been called
+      return;
+  }
+}
+
+// four arguments
+
+template<typename T1, typename T2, typename T3, typename T4>
+inline void
+EventEmitter<T1, T2, T3, T4>::operator+=(Handler handler)
+{
+  m_handlers.push_back(handler);
+}
+
+template<typename T1, typename T2, typename T3, typename T4>
+inline bool
+EventEmitter<T1, T2, T3, T4>::isEmpty() const
+{
+  return m_handlers.empty();
+}
+
+template<typename T1, typename T2, typename T3, typename T4>
+inline void
+EventEmitter<T1, T2, T3, T4>::clear()
+{
+  return m_handlers.clear();
+}
+
+template<typename T1, typename T2, typename T3, typename T4>
+inline void
+EventEmitter<T1, T2, T3, T4>::operator()
+    (const T1& a1, const T2& a2, const T3& a3, const T4& a4)
+{
+  typename std::vector<Handler>::iterator it;
+  for (it = m_handlers.begin(); it != m_handlers.end(); ++it) {
+    (*it)(a1, a2, a3, a4);
+    if (m_handlers.empty()) // .clear has been called
+      return;
+  }
+}
+
+
+} // namespace util
+} // namespace ndn
+
+#endif // NDN_UTIL_EVENT_EMITTER_HPP
diff --git a/tests/unit-tests/util/test-event-emitter.cpp b/tests/unit-tests/util/test-event-emitter.cpp
new file mode 100644
index 0000000..8dd5314
--- /dev/null
+++ b/tests/unit-tests/util/test-event-emitter.cpp
@@ -0,0 +1,275 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2014 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ * This code is actually copied from NFD project (NDN Forwarding Daemon).
+ * We acknowledge the permission of the authors of NFD.
+ */
+/**
+ * Copyright (c) 2014  Regents of the University of California,
+ *                     Arizona Board of Regents,
+ *                     Colorado State University,
+ *                     University Pierre & Marie Curie, Sorbonne University,
+ *                     Washington University in St. Louis,
+ *                     Beijing Institute of Technology
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "util/event-emitter.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace tests {
+
+using ndn::util::EventEmitter;
+
+BOOST_AUTO_TEST_SUITE(UtilEventEmitter)
+
+class EventEmitterTester : noncopyable
+{
+public:
+  EventEmitterTester();
+
+  int m_hit;
+  int m_a1;
+  int m_a2;
+  int m_a3;
+  int m_a4;
+
+  void
+  clear();
+
+  void
+  f0();
+
+  void
+  f1(int a1);
+
+  void
+  f2(int a1, int a2);
+
+  void
+  f3(int a1, int a2, int a3);
+
+  void
+  f4(int a1, int a2, int a3, int a4);
+};
+
+EventEmitterTester::EventEmitterTester()
+{
+  this->clear();
+}
+
+void
+EventEmitterTester::clear()
+{
+  m_hit = 0;
+  m_a1 = 0;
+  m_a2 = 0;
+  m_a3 = 0;
+  m_a4 = 0;
+}
+
+void
+EventEmitterTester::f0()
+{
+  ++m_hit;
+}
+
+void
+EventEmitterTester::f1(int a1)
+{
+  ++m_hit;
+  m_a1 = a1;
+}
+
+void
+EventEmitterTester::f2(int a1, int a2)
+{
+  ++m_hit;
+  m_a1 = a1;
+  m_a2 = a2;
+}
+
+void
+EventEmitterTester::f3(int a1, int a2, int a3)
+{
+  ++m_hit;
+  m_a1 = a1;
+  m_a2 = a2;
+  m_a3 = a3;
+}
+
+void
+EventEmitterTester::f4(int a1, int a2, int a3, int a4)
+{
+  ++m_hit;
+  m_a1 = a1;
+  m_a2 = a2;
+  m_a3 = a3;
+  m_a4 = a4;
+}
+
+static int g_EventEmitterTest_RefObject_copyCount;
+
+class EventEmitterTest_RefObject
+{
+public:
+  EventEmitterTest_RefObject() {}
+
+  EventEmitterTest_RefObject(const EventEmitterTest_RefObject& other);
+};
+
+EventEmitterTest_RefObject::EventEmitterTest_RefObject(const EventEmitterTest_RefObject& other)
+{
+  ++g_EventEmitterTest_RefObject_copyCount;
+}
+
+void
+EventEmitterTest_RefObject_byVal(EventEmitterTest_RefObject a1) {}
+
+void
+EventEmitterTest_RefObject_byRef(const EventEmitterTest_RefObject& a1) {}
+
+
+BOOST_AUTO_TEST_CASE(ZeroListener)
+{
+  EventEmitter<> ee;
+  BOOST_CHECK_NO_THROW(ee());
+}
+
+BOOST_AUTO_TEST_CASE(TwoListeners)
+{
+  EventEmitterTester eet1;
+  EventEmitterTester eet2;
+  EventEmitter<> ee;
+  ee += bind(&EventEmitterTester::f0, &eet1);
+  ee += bind(&EventEmitterTester::f0, &eet2);
+  ee();
+
+  BOOST_CHECK_EQUAL(eet1.m_hit, 1);
+  BOOST_CHECK_EQUAL(eet2.m_hit, 1);
+}
+
+BOOST_AUTO_TEST_CASE(ZeroArgument)
+{
+  EventEmitterTester eet;
+  EventEmitter<> ee;
+  ee += bind(&EventEmitterTester::f0, &eet);
+  ee();
+
+  BOOST_CHECK_EQUAL(eet.m_hit, 1);
+}
+
+BOOST_AUTO_TEST_CASE(OneArgument)
+{
+  EventEmitterTester eet;
+  EventEmitter<int> ee;
+  ee += bind(&EventEmitterTester::f1, &eet, _1);
+  ee(11);
+
+  BOOST_CHECK_EQUAL(eet.m_hit, 1);
+  BOOST_CHECK_EQUAL(eet.m_a1, 11);
+}
+
+BOOST_AUTO_TEST_CASE(TwoArguments)
+{
+  EventEmitterTester eet;
+  EventEmitter<int,int> ee;
+  ee += bind(&EventEmitterTester::f2, &eet, _1, _2);
+  ee(21, 22);
+
+  BOOST_CHECK_EQUAL(eet.m_hit, 1);
+  BOOST_CHECK_EQUAL(eet.m_a1, 21);
+  BOOST_CHECK_EQUAL(eet.m_a2, 22);
+}
+
+BOOST_AUTO_TEST_CASE(ThreeArguments)
+{
+  EventEmitterTester eet;
+  EventEmitter<int,int,int> ee;
+  ee += bind(&EventEmitterTester::f3, &eet, _1, _2, _3);
+  ee(31, 32, 33);
+
+  BOOST_CHECK_EQUAL(eet.m_hit, 1);
+  BOOST_CHECK_EQUAL(eet.m_a1, 31);
+  BOOST_CHECK_EQUAL(eet.m_a2, 32);
+  BOOST_CHECK_EQUAL(eet.m_a3, 33);
+}
+
+BOOST_AUTO_TEST_CASE(FourArguments)
+{
+  EventEmitterTester eet;
+  EventEmitter<int,int,int,int> ee;
+  ee += bind(&EventEmitterTester::f4, &eet, _1, _2, _3, _4);
+  ee(41, 42, 43, 44);
+
+  BOOST_CHECK_EQUAL(eet.m_hit, 1);
+  BOOST_CHECK_EQUAL(eet.m_a1, 41);
+  BOOST_CHECK_EQUAL(eet.m_a2, 42);
+  BOOST_CHECK_EQUAL(eet.m_a3, 43);
+  BOOST_CHECK_EQUAL(eet.m_a4, 44);
+}
+
+// EventEmitter passes arguments by reference,
+// but it also allows a handler that accept arguments by value
+BOOST_AUTO_TEST_CASE(HandlerByVal)
+{
+  EventEmitterTest_RefObject refObject;
+  g_EventEmitterTest_RefObject_copyCount = 0;
+
+  EventEmitter<EventEmitterTest_RefObject> ee;
+  ee += &EventEmitterTest_RefObject_byVal;
+  ee(refObject);
+
+  BOOST_CHECK_EQUAL(g_EventEmitterTest_RefObject_copyCount, 1);
+}
+
+// EventEmitter passes arguments by reference, and no copying
+// is necessary when handler accepts arguments by reference
+BOOST_AUTO_TEST_CASE(HandlerByRef)
+{
+  EventEmitterTest_RefObject refObject;
+  g_EventEmitterTest_RefObject_copyCount = 0;
+
+  EventEmitter<EventEmitterTest_RefObject> ee;
+  ee += &EventEmitterTest_RefObject_byRef;
+  ee(refObject);
+
+  BOOST_CHECK_EQUAL(g_EventEmitterTest_RefObject_copyCount, 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace ndn