util: allow emitSignal with non-zero arguments

refs #2329

Change-Id: I7708878343f87bceff3c52c9266afcfd388e8683
diff --git a/src/util/signal-emit.hpp b/src/util/signal-emit.hpp
index 729d92a..17eacd0 100644
--- a/src/util/signal-emit.hpp
+++ b/src/util/signal-emit.hpp
@@ -51,16 +51,14 @@
  *  \note This macro should be used in 'protected' section so that it's accessible
  *        by derived classes.
  *  \note emit_signalName method is implementation detail.
- *        Derived classes should use 'emit' macro.
+ *        Derived classes should use 'emitSignal' macro.
  *  \note The name 'emit_signalName' is an intentional violation of code-style rule 2.5.
- *  \note The DummyExtraArg is necessary so that 'emit' macro can be used for
- *        both signals with zero arguments and signals with non-zero arguments.
  *  \note The method is declared as a template, so that the macro doesn't need argument types.
  *        But only argument types that are compatible with Signal declaration will work.
  */
 #define DECLARE_SIGNAL_EMIT(signalName) \
   template<typename ...TArgs> \
-  void emit_##signalName(const TArgs&... args, const ::ndn::util::signal::DummyExtraArg&) \
+  void emit_##signalName(const TArgs&... args) \
   { \
     signalName(args...); \
   }
@@ -68,7 +66,7 @@
 /** \brief (implementation detail) invokes emit_signalName method
  *  \note C99 requires at least one argument to be passed in __VA_ARGS__,
  *        thus a DummyExtraArg is expected at the end of __VA_ARGS__,
- *        which will be accepted but ignored by emit_signalName method.
+ *        which will be accepted but ignored by Signal::operator() overload.
  */
 #define NDN_CXX_SIGNAL_EMIT(signalName, ...) \
   emit_##signalName(__VA_ARGS__)
diff --git a/src/util/signal-signal.hpp b/src/util/signal-signal.hpp
index 444c702..e1c01df 100644
--- a/src/util/signal-signal.hpp
+++ b/src/util/signal-signal.hpp
@@ -29,6 +29,8 @@
 namespace util {
 namespace signal {
 
+class DummyExtraArg;
+
 /** \brief provides a lightweight signal / event system
  *
  *  To declare a signal:
@@ -76,6 +78,12 @@
   void
   operator()(const TArgs&...args);
 
+  /** \brief (implementation detail) emits a signal
+   *  \note This overload is used by signal-emit.hpp.
+   */
+  void
+  operator()(const TArgs&...args, const DummyExtraArg&);
+
   // make Owner a friend of Signal<Owner, ...> so that API for owner can be called
 #if NDN_CXX_HAVE_CXX_FRIEND_TYPENAME
   friend Owner;
@@ -208,6 +216,13 @@
   m_isExecuting = false;
 }
 
+template<typename Owner, typename ...TArgs>
+inline void
+Signal<Owner, TArgs...>::operator()(const TArgs&... args, const DummyExtraArg&)
+{
+  this->operator()(args...);
+}
+
 } // namespace signal
 
 // expose as ndn::util::Signal
diff --git a/tests/unit-tests/util/signal.cpp b/tests/unit-tests/util/signal.cpp
index afb4980..e0d70aa 100644
--- a/tests/unit-tests/util/signal.cpp
+++ b/tests/unit-tests/util/signal.cpp
@@ -68,6 +68,39 @@
   BOOST_CHECK_EQUAL(hit2, 1);
 }
 
+class SignalOwner1
+{
+public:
+  Signal<SignalOwner1, int> sig;
+
+protected:
+  DECLARE_SIGNAL_EMIT(sig)
+};
+
+class SignalEmitter1 : public SignalOwner1
+{
+public:
+  void
+  emitTestSignal()
+  {
+    this->emitSignal(sig, 8106);
+  }
+};
+
+BOOST_AUTO_TEST_CASE(OneArgument)
+{
+  SignalEmitter1 se;
+
+  int hit = 0;
+  se.sig.connect([&hit] (int a) {
+    ++hit;
+    BOOST_CHECK_EQUAL(a, 8106);
+  });
+  se.emitTestSignal();
+
+  BOOST_CHECK_EQUAL(hit, 1);
+}
+
 BOOST_AUTO_TEST_CASE(TwoArguments)
 {
   Signal<std::remove_pointer<decltype(this)>::type, int, int> sig;