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