util: partial support for abstract types in NDN_LOG_MEMBER_* macros

Change-Id: I6b2a0e8fb86cb83040d24e3d4d5ba8a872ca031c
diff --git a/ndn-cxx/util/logger.hpp b/ndn-cxx/util/logger.hpp
index 30b8779..81c1076 100644
--- a/ndn-cxx/util/logger.hpp
+++ b/ndn-cxx/util/logger.hpp
@@ -113,7 +113,13 @@
 struct ExtractArgument;
 
 template<class T, class U>
-struct ExtractArgument<T(U)>
+struct ExtractArgument<T(U&)>
+{
+  using type = U;
+};
+
+template<class T, class U>
+struct ExtractArgument<T(U)&>
 {
   using type = U;
 };
@@ -190,9 +196,9 @@
  *        a dot (`.`), or contain multiple consecutive dots.
  */
 #define NDN_LOG_MEMBER_INIT(cls, name) \
-  const bool ::ndn::util::detail::ArgumentType<void(cls)>::ndn_cxx_loggerRegistration = \
+  const bool ::ndn::util::detail::ArgumentType<void(cls&)>::ndn_cxx_loggerRegistration = \
   NDN_LOG_REGISTER_NAME(name); \
-  ::ndn::util::Logger& ::ndn::util::detail::ArgumentType<void(cls)>::ndn_cxx_getLogger() \
+  ::ndn::util::Logger& ::ndn::util::detail::ArgumentType<void(cls&)>::ndn_cxx_getLogger() \
   NDN_LOG_INIT_FUNCTION_BODY(name) \
   struct ndn_cxx_allow_trailing_semicolon
 
@@ -202,9 +208,9 @@
  */
 #define NDN_LOG_MEMBER_DECL_SPECIALIZED(cls) \
   template<> \
-  const bool ::ndn::util::detail::ArgumentType<void(cls)>::ndn_cxx_loggerRegistration; \
+  const bool ::ndn::util::detail::ArgumentType<void(cls&)>::ndn_cxx_loggerRegistration; \
   template<> \
-  ::ndn::util::Logger& ::ndn::util::detail::ArgumentType<void(cls)>::ndn_cxx_getLogger()
+  ::ndn::util::Logger& ::ndn::util::detail::ArgumentType<void(cls&)>::ndn_cxx_getLogger()
 
 /** \brief Define an explicit specialization of a member log module of a class template.
  *
@@ -219,10 +225,10 @@
  */
 #define NDN_LOG_MEMBER_INIT_SPECIALIZED(cls, name) \
   template<> \
-  const bool ::ndn::util::detail::ArgumentType<void(cls)>::ndn_cxx_loggerRegistration = \
+  const bool ::ndn::util::detail::ArgumentType<void(cls&)>::ndn_cxx_loggerRegistration = \
   NDN_LOG_REGISTER_NAME(name); \
   template<> \
-  ::ndn::util::Logger& ::ndn::util::detail::ArgumentType<void(cls)>::ndn_cxx_getLogger() \
+  ::ndn::util::Logger& ::ndn::util::detail::ArgumentType<void(cls&)>::ndn_cxx_getLogger() \
   NDN_LOG_INIT_FUNCTION_BODY(name) \
   struct ndn_cxx_allow_trailing_semicolon
 
diff --git a/tests/unit/util/logging.t.cpp b/tests/unit/util/logging.t.cpp
index e051cce..b8034b8 100644
--- a/tests/unit/util/logging.t.cpp
+++ b/tests/unit/util/logging.t.cpp
@@ -103,6 +103,38 @@
 
 NDN_LOG_MEMBER_INIT(ClassWithLogger, ndn.util.tests.ClassWithLogger);
 
+class AbstractClassWithLogger
+{
+public:
+  virtual
+  ~AbstractClassWithLogger() = default;
+
+  void
+  logFromConstMemberFunction() const
+  {
+    NDN_LOG_INFO("const member function");
+  }
+
+  virtual void
+  logFromVirtualFunction() = 0;
+
+protected:
+  NDN_LOG_MEMBER_DECL();
+};
+
+// Check that the macro can cope with abstract types
+NDN_LOG_MEMBER_INIT(AbstractClassWithLogger, ndn.util.tests.AbstractClassWithLogger);
+
+class DerivedClass : public AbstractClassWithLogger
+{
+public:
+  void
+  logFromVirtualFunction() final
+  {
+    NDN_LOG_INFO("overridden virtual function");
+  }
+};
+
 template<class T, class U>
 class ClassTemplateWithLogger
 {
@@ -301,11 +333,13 @@
 BOOST_AUTO_TEST_CASE(MemberLogger)
 {
   Logging::setLevel("ndn.util.tests.ClassWithLogger", LogLevel::INFO);
+  Logging::setLevel("ndn.util.tests.AbstractClassWithLogger", LogLevel::INFO);
   Logging::setLevel("ndn.util.tests.Specialized1", LogLevel::INFO);
   // ndn.util.tests.Specialized2 is not enabled
 
   const auto& names = Logging::getLoggerNames();
   BOOST_CHECK_EQUAL(names.count("ndn.util.tests.ClassWithLogger"), 1);
+  BOOST_CHECK_EQUAL(names.count("ndn.util.tests.AbstractClassWithLogger"), 1);
   BOOST_CHECK_EQUAL(names.count("ndn.util.tests.Specialized1"), 1);
   BOOST_CHECK_EQUAL(names.count("ndn.util.tests.Specialized2"), 1);
 
@@ -318,6 +352,15 @@
     LOG_SYSTIME_STR + "  INFO: [ndn.util.tests.ClassWithLogger] const member function\n"
     ));
 
+  DerivedClass{}.logFromConstMemberFunction();
+  DerivedClass{}.logFromVirtualFunction();
+
+  Logging::flush();
+  BOOST_CHECK(os.is_equal(
+    LOG_SYSTIME_STR + "  INFO: [ndn.util.tests.AbstractClassWithLogger] const member function\n" +
+    LOG_SYSTIME_STR + "  INFO: [ndn.util.tests.AbstractClassWithLogger] overridden virtual function\n"
+    ));
+
   ClassTemplateWithLogger<int, double>::logFromStaticMemberFunction();
   ClassTemplateWithLogger<int, double>{}.logFromMemberFunction();
   ClassTemplateWithLogger<int, std::string>::logFromStaticMemberFunction();
@@ -646,7 +689,7 @@
   BOOST_CHECK(os2weak.expired());
 }
 
-BOOST_AUTO_TEST_CASE(SetNullptrDestination)
+BOOST_AUTO_TEST_CASE(NullDestination)
 {
   Logging::setDestination(nullptr);
   logFromModule1();