fw: refactor Strategy registry

Strategy registry is moved into Strategy class.
Strategy instantiation logic is implemented in Strategy::create.
These are in preparation of supporting Strategy parameters.

refs #3868

Change-Id: If36a08ad25a00a7008a5eccc8cfe4f6c63638676
diff --git a/daemon/fw/forwarder.cpp b/daemon/fw/forwarder.cpp
index 7e0df65..7482cf4 100644
--- a/daemon/fw/forwarder.cpp
+++ b/daemon/fw/forwarder.cpp
@@ -25,8 +25,9 @@
 
 #include "forwarder.hpp"
 #include "algorithm.hpp"
-#include "core/logger.hpp"
+#include "best-route-strategy2.hpp"
 #include "strategy.hpp"
+#include "core/logger.hpp"
 #include "table/cleanup.hpp"
 #include <ndn-cxx/lp/tags.hpp>
 
@@ -34,15 +35,19 @@
 
 NFD_LOG_INIT("Forwarder");
 
+static Name
+getDefaultStrategyName()
+{
+  return fw::BestRouteStrategy2::STRATEGY_NAME;
+}
+
 Forwarder::Forwarder()
   : m_unsolicitedDataPolicy(new fw::DefaultUnsolicitedDataPolicy())
   , m_fib(m_nameTree)
   , m_pit(m_nameTree)
   , m_measurements(m_nameTree)
-  , m_strategyChoice(m_nameTree, fw::makeDefaultStrategy(*this))
+  , m_strategyChoice(*this)
 {
-  fw::installStrategies(*this);
-
   m_faceTable.afterAdd.connect([this] (Face& face) {
     face.afterReceiveInterest.connect(
       [this, &face] (const Interest& interest) {
@@ -61,6 +66,9 @@
   m_faceTable.beforeRemove.connect([this] (Face& face) {
     cleanupOnFaceRemoval(m_nameTree, m_fib, m_pit, face);
   });
+
+  m_strategyChoice.setDefaultStrategy(getDefaultStrategyName());
+  m_strategyChoice.installFromRegistry();
 }
 
 Forwarder::~Forwarder() = default;
diff --git a/daemon/fw/strategy-registry.cpp b/daemon/fw/strategy-registry.cpp
deleted file mode 100644
index 49a778f..0000000
--- a/daemon/fw/strategy-registry.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2016,  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,
- *                           The University of Memphis.
- *
- * 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 "strategy-registry.hpp"
-#include "best-route-strategy2.hpp"
-
-namespace nfd {
-namespace fw {
-
-unique_ptr<Strategy>
-makeDefaultStrategy(Forwarder& forwarder)
-{
-  return make_unique<BestRouteStrategy2>(ref(forwarder));
-}
-
-static std::map<Name, StrategyCreateFunc>&
-getStrategyFactories()
-{
-  static std::map<Name, StrategyCreateFunc> strategyFactories;
-  return strategyFactories;
-}
-
-void
-registerStrategyImpl(const Name& strategyName, const StrategyCreateFunc& createFunc)
-{
-  getStrategyFactories().insert({strategyName, createFunc});
-}
-
-void
-installStrategies(Forwarder& forwarder)
-{
-  StrategyChoice& sc = forwarder.getStrategyChoice();
-  for (const auto& pair : getStrategyFactories()) {
-    if (!sc.hasStrategy(pair.first, true)) {
-      sc.install(pair.second(forwarder));
-    }
-  }
-}
-
-} // namespace fw
-} // namespace nfd
diff --git a/daemon/fw/strategy-registry.hpp b/daemon/fw/strategy-registry.hpp
deleted file mode 100644
index 83e9f73..0000000
--- a/daemon/fw/strategy-registry.hpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2016,  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,
- *                           The University of Memphis.
- *
- * 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 NFD_DAEMON_FW_STRATEGY_REGISTRY_HPP
-#define NFD_DAEMON_FW_STRATEGY_REGISTRY_HPP
-
-#include "core/common.hpp"
-
-namespace nfd {
-
-class Forwarder;
-
-namespace fw {
-
-class Strategy;
-
-unique_ptr<Strategy>
-makeDefaultStrategy(Forwarder& forwarder);
-
-void
-installStrategies(Forwarder& forwarder);
-
-
-typedef std::function<unique_ptr<Strategy>(Forwarder&)> StrategyCreateFunc;
-
-void
-registerStrategyImpl(const Name& strategyName, const StrategyCreateFunc& createFunc);
-
-/** \brief registers a strategy to be installed later
- */
-template<typename S>
-void
-registerStrategy()
-{
-  registerStrategyImpl(S::STRATEGY_NAME,
-                       [] (Forwarder& forwarder) { return make_unique<S>(ref(forwarder)); });
-}
-
-/** \brief registers a built-in strategy
- *
- *  This macro should appear once in .cpp of each built-in strategy.
- */
-#define NFD_REGISTER_STRATEGY(StrategyType)                       \
-static class NfdAuto ## StrategyType ## StrategyRegistrationClass \
-{                                                                 \
-public:                                                           \
-  NfdAuto ## StrategyType ## StrategyRegistrationClass()          \
-  {                                                               \
-    ::nfd::fw::registerStrategy<StrategyType>();                  \
-  }                                                               \
-} g_nfdAuto ## StrategyType ## StrategyRegistrationVariable
-
-} // namespace fw
-} // namespace nfd
-
-#endif // NFD_DAEMON_FW_STRATEGY_REGISTRY_HPP
diff --git a/daemon/fw/strategy.cpp b/daemon/fw/strategy.cpp
index 218ce44..ae8a17c 100644
--- a/daemon/fw/strategy.cpp
+++ b/daemon/fw/strategy.cpp
@@ -27,12 +27,70 @@
 #include "forwarder.hpp"
 #include "core/logger.hpp"
 #include "core/random.hpp"
+#include <boost/range/adaptor/map.hpp>
+#include <boost/range/algorithm/copy.hpp>
 
 namespace nfd {
 namespace fw {
 
 NFD_LOG_INIT("Strategy");
 
+Strategy::Registry&
+Strategy::getRegistry()
+{
+  static Registry registry;
+  return registry;
+}
+
+Strategy::Registry::const_iterator
+Strategy::find(const Name& strategyName)
+{
+  const Registry& registry = getRegistry();
+  Registry::const_iterator candidate = registry.end();
+  for (auto it = registry.lower_bound(strategyName);
+       it != registry.end() && strategyName.isPrefixOf(it->first); ++it) {
+    switch (it->first.size() - strategyName.size()) {
+      case 0: // exact match
+        return it;
+      case 1: // unversioned strategyName matches versioned strategy
+        candidate = it;
+        break;
+    }
+  }
+  return candidate;
+  ///\todo #3868 if exact version unavailable, choose closest higher version
+  ///\todo #3868 allow parameter component
+}
+
+bool
+Strategy::canCreate(const Name& strategyName)
+{
+  return Strategy::find(strategyName) != getRegistry().end();
+}
+
+unique_ptr<Strategy>
+Strategy::create(const Name& strategyName, Forwarder& forwarder)
+{
+  auto found = Strategy::find(strategyName);
+  if (found == getRegistry().end()) {
+    NFD_LOG_DEBUG("create " << strategyName << " not-found");
+    return nullptr;
+  }
+
+  NFD_LOG_DEBUG("create " << strategyName << " found=" << found->first);
+  ///\todo #3868 pass parameters to strategy constructor
+  return found->second(forwarder, found->first);
+}
+
+std::set<Name>
+Strategy::listRegistered()
+{
+  std::set<Name> strategyNames;
+  boost::copy(getRegistry() | boost::adaptors::map_keys,
+              std::inserter(strategyNames, strategyNames.end()));
+  return strategyNames;
+}
+
 Strategy::Strategy(Forwarder& forwarder, const Name& name)
   : afterAddFace(forwarder.getFaceTable().afterAdd)
   , beforeRemoveFace(forwarder.getFaceTable().beforeRemove)
diff --git a/daemon/fw/strategy.hpp b/daemon/fw/strategy.hpp
index f4ef3b1..0bc8d9e 100644
--- a/daemon/fw/strategy.hpp
+++ b/daemon/fw/strategy.hpp
@@ -27,7 +27,6 @@
 #define NFD_DAEMON_FW_STRATEGY_HPP
 
 #include "forwarder.hpp"
-#include "strategy-registry.hpp"
 #include "table/measurements-accessor.hpp"
 
 namespace nfd {
@@ -35,9 +34,47 @@
 
 /** \brief represents a forwarding strategy
  */
-class Strategy : public enable_shared_from_this<Strategy>, noncopyable
+class Strategy : noncopyable
 {
-public:
+public: // registry
+  /** \brief register a strategy type
+   *  \tparam S subclass of Strategy
+   *  \param strategyName versioned strategy name
+   *  \note It is permitted to register the same strategy type under multiple names,
+   *        which is useful in tests and for creating aliases.
+   */
+  template<typename S>
+  static void
+  registerType(const Name& strategyName = S::STRATEGY_NAME)
+  {
+    Registry& registry = getRegistry();
+    BOOST_ASSERT(registry.count(strategyName) == 0);
+    registry[strategyName] = &make_unique<S, Forwarder&, const Name&>;
+  }
+
+  /** \return whether a strategy instance can be created from \p strategyName
+   *  \param strategyName strategy name, may contain version
+   *  \todo #3868 may contain parameters
+   *  \note This function finds a strategy type using same rules as \p create ,
+   *        but does not attempt to construct an instance.
+   */
+  static bool
+  canCreate(const Name& strategyName);
+
+  /** \return a strategy instance created from \p strategyName,
+   *  \retval nullptr if !canCreate(strategyName)
+   *  \todo #3868 throw std::invalid_argument strategy type constructor
+   *              does not accept specified version or parameters
+   */
+  static unique_ptr<Strategy>
+  create(const Name& strategyName, Forwarder& forwarder);
+
+  /** \return registered versioned strategy names
+   */
+  static std::set<Name>
+  listRegistered();
+
+public: // constructor, destructor, strategy name
   /** \brief construct a strategy instance
    *  \param forwarder a reference to the Forwarder, used to enable actions and accessors.
    *         Strategy subclasses should pass this reference,
@@ -205,7 +242,17 @@
   signal::Signal<FaceTable, Face&>& afterAddFace;
   signal::Signal<FaceTable, Face&>& beforeRemoveFace;
 
-private:
+private: // registry
+  typedef std::function<unique_ptr<Strategy>(Forwarder& forwarder, const Name& strategyName)> CreateFunc;
+  typedef std::map<Name, CreateFunc> Registry; // indexed by strategy name
+
+  static Registry&
+  getRegistry();
+
+  static Registry::const_iterator
+  find(const Name& strategyName);
+
+private: // instance fields
   Name m_name;
 
   /** \brief reference to the forwarder
@@ -220,4 +267,18 @@
 } // namespace fw
 } // namespace nfd
 
+/** \brief registers a strategy
+ *
+ *  This macro should appear once in .cpp of each strategy.
+ */
+#define NFD_REGISTER_STRATEGY(S)                       \
+static class NfdAuto ## S ## StrategyRegistrationClass \
+{                                                      \
+public:                                                \
+  NfdAuto ## S ## StrategyRegistrationClass()          \
+  {                                                    \
+    ::nfd::fw::Strategy::registerType<S>();            \
+  }                                                    \
+} g_nfdAuto ## S ## StrategyRegistrationVariable
+
 #endif // NFD_DAEMON_FW_STRATEGY_HPP
diff --git a/daemon/table/strategy-choice.cpp b/daemon/table/strategy-choice.cpp
index c3d02e3..81de636 100644
--- a/daemon/table/strategy-choice.cpp
+++ b/daemon/table/strategy-choice.cpp
@@ -24,10 +24,10 @@
  */
 
 #include "strategy-choice.hpp"
+#include "measurements-entry.hpp"
+#include "pit-entry.hpp"
 #include "core/logger.hpp"
 #include "fw/strategy.hpp"
-#include "pit-entry.hpp"
-#include "measurements-entry.hpp"
 
 namespace nfd {
 namespace strategy_choice {
@@ -42,11 +42,48 @@
   return nte.getStrategyChoiceEntry() != nullptr;
 }
 
-StrategyChoice::StrategyChoice(NameTree& nameTree, unique_ptr<Strategy> defaultStrategy)
-  : m_nameTree(nameTree)
+StrategyChoice::StrategyChoice(Forwarder& forwarder)
+  : m_forwarder(forwarder)
+  , m_nameTree(m_forwarder.getNameTree())
   , m_nItems(0)
 {
-  this->setDefaultStrategy(std::move(defaultStrategy));
+}
+
+void
+StrategyChoice::setDefaultStrategy(const Name& strategyName)
+{
+  unique_ptr<Strategy> strategy = Strategy::create(strategyName, m_forwarder);
+
+  bool isInstalled = false;
+  Strategy* instance = nullptr;
+  std::tie(isInstalled, instance) = this->install(std::move(strategy));
+  BOOST_ASSERT(isInstalled);
+
+  auto entry = make_unique<Entry>(Name());
+  entry->setStrategy(*instance);
+
+  // don't use .insert here, because it will invoke findEffectiveStrategy
+  // which expects an existing root entry
+  name_tree::Entry& nte = m_nameTree.lookup(Name());
+  nte.setStrategyChoiceEntry(std::move(entry));
+  ++m_nItems;
+  NFD_LOG_INFO("setDefaultStrategy " << instance->getName());
+}
+
+void
+StrategyChoice::installFromRegistry()
+{
+  /// \todo #3868
+  /// * Give every StrategyChoice entry its own Strategy instance.
+  ///    Don't share Strategy instances.
+  /// * Create Strategy instance with Strategy::create.
+  /// * Eliminate install, installFromRegistry, getStrategy functions.
+
+  // This is temporary code until the above is done.
+
+  for (const Name& strategyName : Strategy::listRegistered()) {
+    this->install(Strategy::create(strategyName, m_forwarder));
+  }
 }
 
 bool
@@ -198,25 +235,6 @@
   return this->findEffectiveStrategyImpl(measurementsEntry);
 }
 
-void
-StrategyChoice::setDefaultStrategy(unique_ptr<Strategy> strategy)
-{
-  bool isInstalled = false;
-  Strategy* instance = nullptr;
-  std::tie(isInstalled, instance) = this->install(std::move(strategy));
-  BOOST_ASSERT(isInstalled);
-
-  auto entry = make_unique<Entry>(Name());
-  entry->setStrategy(*instance);
-
-  // don't use .insert here, because it will invoke findEffectiveStrategy
-  // which expects an existing root entry
-  name_tree::Entry& nte = m_nameTree.lookup(Name());
-  nte.setStrategyChoiceEntry(std::move(entry));
-  ++m_nItems;
-  NFD_LOG_INFO("setDefaultStrategy " << instance->getName());
-}
-
 static inline void
 clearStrategyInfo(const name_tree::Entry& nte)
 {
diff --git a/daemon/table/strategy-choice.hpp b/daemon/table/strategy-choice.hpp
index 1038cfb..f2410e2 100644
--- a/daemon/table/strategy-choice.hpp
+++ b/daemon/table/strategy-choice.hpp
@@ -32,6 +32,9 @@
 #include <boost/range/adaptor/transformed.hpp>
 
 namespace nfd {
+
+class Forwarder;
+
 namespace strategy_choice {
 
 /** \brief represents the Strategy Choice table
@@ -48,7 +51,8 @@
 class StrategyChoice : noncopyable
 {
 public:
-  StrategyChoice(NameTree& nameTree, unique_ptr<fw::Strategy> defaultStrategy);
+  explicit
+  StrategyChoice(Forwarder& forwarder);
 
   size_t
   size() const
@@ -56,7 +60,18 @@
     return m_nItems;
   }
 
-public: // available Strategy types
+  /** \brief set the default strategy
+   *
+   *  This must be called by forwarder constructor.
+   */
+  void
+  setDefaultStrategy(const Name& strategyName);
+
+  /** \brief install all strategies from registry
+   */
+  void
+  installFromRegistry();
+
   /** \brief determines if a strategy is installed
    *  \param strategyName name of the strategy
    *  \param isExact true to require exact match, false to permit unversioned strategyName
@@ -152,9 +167,6 @@
   getStrategy(const Name& strategyName) const;
 
   void
-  setDefaultStrategy(unique_ptr<fw::Strategy> strategy);
-
-  void
   changeStrategy(Entry& entry,
                  fw::Strategy& oldStrategy,
                  fw::Strategy& newStrategy);
@@ -169,6 +181,7 @@
   getRange() const;
 
 private:
+  Forwarder& m_forwarder;
   NameTree& m_nameTree;
   size_t m_nItems;
 
diff --git a/tests/daemon/fw/access-strategy.t.cpp b/tests/daemon/fw/access-strategy.t.cpp
index b2dfe50..c0a0240 100644
--- a/tests/daemon/fw/access-strategy.t.cpp
+++ b/tests/daemon/fw/access-strategy.t.cpp
@@ -50,6 +50,11 @@
 BOOST_AUTO_TEST_SUITE(Fw)
 BOOST_FIXTURE_TEST_SUITE(TestAccessStrategy, UnitTestTimeFixture)
 
+BOOST_AUTO_TEST_CASE(Registration)
+{
+  BOOST_CHECK_EQUAL(Strategy::listRegistered().count(AccessStrategy::STRATEGY_NAME), 1);
+}
+
 class TwoLaptopsFixture : public UnitTestTimeFixture
 {
 protected:
diff --git a/tests/daemon/fw/asf-strategy.t.cpp b/tests/daemon/fw/asf-strategy.t.cpp
index dddacf1..c0aa337 100644
--- a/tests/daemon/fw/asf-strategy.t.cpp
+++ b/tests/daemon/fw/asf-strategy.t.cpp
@@ -38,6 +38,11 @@
 BOOST_AUTO_TEST_SUITE(Fw)
 BOOST_FIXTURE_TEST_SUITE(TestAsfStrategy, UnitTestTimeFixture)
 
+BOOST_AUTO_TEST_CASE(Registration)
+{
+  BOOST_CHECK_EQUAL(Strategy::listRegistered().count(AsfStrategy::STRATEGY_NAME), 1);
+}
+
 class AsfGridFixture : public UnitTestTimeFixture
 {
 protected:
diff --git a/tests/daemon/fw/best-route-strategy2.t.cpp b/tests/daemon/fw/best-route-strategy2.t.cpp
index ec6e951..b8406b5 100644
--- a/tests/daemon/fw/best-route-strategy2.t.cpp
+++ b/tests/daemon/fw/best-route-strategy2.t.cpp
@@ -73,6 +73,11 @@
 
 BOOST_FIXTURE_TEST_SUITE(TestBestRouteStrategy2, BestRouteStrategy2Fixture)
 
+BOOST_AUTO_TEST_CASE(Registration)
+{
+  BOOST_CHECK_EQUAL(Strategy::listRegistered().count(BestRouteStrategy2::STRATEGY_NAME), 1);
+}
+
 BOOST_AUTO_TEST_CASE(Forward)
 {
   fib::Entry& fibEntry = *fib.insert(Name()).first;
diff --git a/tests/daemon/fw/multicast-strategy.t.cpp b/tests/daemon/fw/multicast-strategy.t.cpp
index 1b8afcb..a5255b5 100644
--- a/tests/daemon/fw/multicast-strategy.t.cpp
+++ b/tests/daemon/fw/multicast-strategy.t.cpp
@@ -38,6 +38,11 @@
 BOOST_AUTO_TEST_SUITE(Fw)
 BOOST_FIXTURE_TEST_SUITE(TestMulticastStrategy, BaseFixture)
 
+BOOST_AUTO_TEST_CASE(Registration)
+{
+  BOOST_CHECK_EQUAL(Strategy::listRegistered().count(MulticastStrategy::STRATEGY_NAME), 1);
+}
+
 BOOST_AUTO_TEST_CASE(Forward2)
 {
   Forwarder forwarder;
diff --git a/tests/daemon/fw/ncc-strategy.t.cpp b/tests/daemon/fw/ncc-strategy.t.cpp
index cc32201..a792404 100644
--- a/tests/daemon/fw/ncc-strategy.t.cpp
+++ b/tests/daemon/fw/ncc-strategy.t.cpp
@@ -40,7 +40,10 @@
 BOOST_AUTO_TEST_SUITE(Fw)
 BOOST_FIXTURE_TEST_SUITE(TestNccStrategy, UnitTestTimeFixture)
 
-using fw::NccStrategy;
+BOOST_AUTO_TEST_CASE(Registration)
+{
+  BOOST_CHECK_EQUAL(Strategy::listRegistered().count(NccStrategy::STRATEGY_NAME), 1);
+}
 
 // NccStrategy is fairly complex.
 // The most important property is: