face: ProtocolFactory registry
refs #3904
Change-Id: Ic7c8b3d6138b7c27d4189a2e15cc646055ad1294
diff --git a/daemon/face/ethernet-factory.cpp b/daemon/face/ethernet-factory.cpp
index 53a0815..8d3e737 100644
--- a/daemon/face/ethernet-factory.cpp
+++ b/daemon/face/ethernet-factory.cpp
@@ -34,6 +34,15 @@
namespace face {
NFD_LOG_INIT("EthernetFactory");
+NFD_REGISTER_PROTOCOL_FACTORY(EthernetFactory);
+
+const std::string&
+EthernetFactory::getId()
+{
+ static std::string id("ether");
+ return id;
+}
+
void
EthernetFactory::processConfig(OptionalConfigSection configSection,
diff --git a/daemon/face/ethernet-factory.hpp b/daemon/face/ethernet-factory.hpp
index 1dec480..d1b9ee2 100644
--- a/daemon/face/ethernet-factory.hpp
+++ b/daemon/face/ethernet-factory.hpp
@@ -38,6 +38,9 @@
class EthernetFactory : public ProtocolFactory
{
public:
+ static const std::string&
+ getId();
+
/** \brief process face_system.ether config section
*/
void
diff --git a/daemon/face/face-system.cpp b/daemon/face/face-system.cpp
index 5359e2d..a955d97 100644
--- a/daemon/face/face-system.cpp
+++ b/daemon/face/face-system.cpp
@@ -24,54 +24,31 @@
*/
#include "face-system.hpp"
-// #include "core/logger.hpp"
+#include "protocol-factory.hpp"
+#include "core/logger.hpp"
#include "fw/face-table.hpp"
-// ProtocolFactory includes, sorted alphabetically
-#ifdef HAVE_LIBPCAP
-#include "ethernet-factory.hpp"
-#endif // HAVE_LIBPCAP
-#include "tcp-factory.hpp"
-#include "udp-factory.hpp"
-#ifdef HAVE_UNIX_SOCKETS
-#include "unix-stream-factory.hpp"
-#endif // HAVE_UNIX_SOCKETS
-#ifdef HAVE_WEBSOCKET
-#include "websocket-factory.hpp"
-#endif // HAVE_WEBSOCKET
-
namespace nfd {
namespace face {
-// NFD_LOG_INIT("FaceSystem");
+NFD_LOG_INIT("FaceSystem");
FaceSystem::FaceSystem(FaceTable& faceTable)
: m_faceTable(faceTable)
{
- ///\todo #3904 make a registry, and construct instances from registry
-
-#ifdef HAVE_LIBPCAP
- m_factories["ether"] = make_shared<EthernetFactory>();
-#endif // HAVE_LIBPCAP
-
- m_factories["tcp"] = make_shared<TcpFactory>();
-
- m_factories["udp"] = make_shared<UdpFactory>();
-
-#ifdef HAVE_UNIX_SOCKETS
- m_factories["unix"] = make_shared<UnixStreamFactory>();
-#endif // HAVE_UNIX_SOCKETS
-
-#ifdef HAVE_WEBSOCKET
- m_factories["websocket"] = make_shared<WebSocketFactory>();
-#endif // HAVE_WEBSOCKET
+ for (const std::string& id : ProtocolFactory::listRegistered()) {
+ NFD_LOG_TRACE("creating factory " << id);
+ m_factories[id] = ProtocolFactory::create(id);
+ }
}
+FaceSystem::~FaceSystem() = default;
+
std::set<const ProtocolFactory*>
FaceSystem::listProtocolFactories() const
{
std::set<const ProtocolFactory*> factories;
- for (const auto& p : m_factoryByScheme) {
+ for (const auto& p : m_factories) {
factories.insert(p.second.get());
}
return factories;
@@ -88,7 +65,7 @@
FaceSystem::getFactoryByScheme(const std::string& scheme)
{
auto found = m_factoryByScheme.find(scheme);
- return found == m_factoryByScheme.end() ? nullptr : found->second.get();
+ return found == m_factoryByScheme.end() ? nullptr : found->second;
}
void
@@ -108,7 +85,7 @@
// process sections in protocol factories
for (const auto& pair : m_factories) {
const std::string& sectionName = pair.first;
- shared_ptr<ProtocolFactory> factory = pair.second;
+ ProtocolFactory* factory = pair.second.get();
std::set<std::string> oldProvidedSchemes = factory->getProvidedSchemes();
factory->processConfig(configSection.get_child_optional(sectionName), context);
@@ -116,10 +93,15 @@
if (!isDryRun) {
for (const std::string& scheme : factory->getProvidedSchemes()) {
m_factoryByScheme[scheme] = factory;
- oldProvidedSchemes.erase(scheme);
+ if (oldProvidedSchemes.erase(scheme) == 0) {
+ NFD_LOG_TRACE("factory " << sectionName <<
+ " provides " << scheme << " FaceUri scheme");
+ }
}
for (const std::string& scheme : oldProvidedSchemes) {
m_factoryByScheme.erase(scheme);
+ NFD_LOG_TRACE("factory " << sectionName <<
+ " no longer provides " << scheme << " FaceUri scheme");
}
}
}
diff --git a/daemon/face/face-system.hpp b/daemon/face/face-system.hpp
index f6a7c71..bca7702 100644
--- a/daemon/face/face-system.hpp
+++ b/daemon/face/face-system.hpp
@@ -50,6 +50,8 @@
explicit
FaceSystem(FaceTable& faceTable);
+ ~FaceSystem();
+
/** \return ProtocolFactory objects owned by the FaceSystem
*/
std::set<const ProtocolFactory*>
@@ -100,17 +102,15 @@
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
/** \brief config section name => protocol factory
- *
- * \todo #3904 store unique_ptr<ProtocolFactory> here, and reference_wrapper<ProtocolFactory>
- * in m_factoryByScheme
*/
- std::map<std::string, shared_ptr<ProtocolFactory>> m_factories;
+ std::map<std::string, unique_ptr<ProtocolFactory>> m_factories;
+private:
/** \brief scheme => protocol factory
*
* The same protocol factory may be available under multiple schemes.
*/
- std::map<std::string, shared_ptr<ProtocolFactory>> m_factoryByScheme;
+ std::map<std::string, ProtocolFactory*> m_factoryByScheme;
FaceTable& m_faceTable;
};
diff --git a/daemon/face/protocol-factory.cpp b/daemon/face/protocol-factory.cpp
new file mode 100644
index 0000000..daa0214
--- /dev/null
+++ b/daemon/face/protocol-factory.cpp
@@ -0,0 +1,58 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2017, 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 "protocol-factory.hpp"
+#include <boost/range/adaptor/map.hpp>
+#include <boost/range/algorithm/copy.hpp>
+
+namespace nfd {
+namespace face {
+
+ProtocolFactory::Registry&
+ProtocolFactory::getRegistry()
+{
+ static Registry registry;
+ return registry;
+}
+
+unique_ptr<ProtocolFactory>
+ProtocolFactory::create(const std::string& id)
+{
+ Registry& registry = getRegistry();
+ auto found = registry.find(id);
+ return found == registry.end() ? nullptr : found->second();
+}
+
+std::set<std::string>
+ProtocolFactory::listRegistered()
+{
+ std::set<std::string> factoryIds;
+ boost::copy(getRegistry() | boost::adaptors::map_keys,
+ std::inserter(factoryIds, factoryIds.end()));
+ return factoryIds;
+}
+
+} // namespace face
+} // namespace nfd
diff --git a/daemon/face/protocol-factory.hpp b/daemon/face/protocol-factory.hpp
index 328d46e..48f755a 100644
--- a/daemon/face/protocol-factory.hpp
+++ b/daemon/face/protocol-factory.hpp
@@ -44,6 +44,31 @@
*/
class ProtocolFactory : noncopyable
{
+public: // registry
+ /** \brief register a protocol factory type
+ * \tparam S subclass of ProtocolFactory
+ * \param id factory identifier
+ */
+ template<typename PF>
+ static void
+ registerType(const std::string& id = PF::getId())
+ {
+ Registry& registry = getRegistry();
+ BOOST_ASSERT(registry.count(id) == 0);
+ registry[id] = &make_unique<PF>;
+ }
+
+ /** \return a protocol factory instance
+ * \retval nullptr if factory with \p id is not registered
+ */
+ static unique_ptr<ProtocolFactory>
+ create(const std::string& id);
+
+ /** \return registered protocol factory ids
+ */
+ static std::set<std::string>
+ listRegistered();
+
public:
/**
* \brief Base class for all exceptions thrown by protocol factories
@@ -58,6 +83,18 @@
}
};
+ virtual
+ ~ProtocolFactory() = default;
+
+#ifdef DOXYGEN
+ /** \return protocol factory id
+ *
+ * face_system.factory-id config section is processed by the protocol factory.
+ */
+ static const std::string&
+ getId();
+#endif
+
/** \brief process face_system subsection that corresponds to this ProtocolFactory type
* \param configSection the configuration section or boost::null to indicate it is omitted
* \param context provides access to data structures and contextual information
@@ -114,6 +151,13 @@
return channels;
}
+private: // registry
+ typedef std::function<unique_ptr<ProtocolFactory>()> CreateFunc;
+ typedef std::map<std::string, CreateFunc> Registry; // indexed by factory id
+
+ static Registry&
+ getRegistry();
+
protected:
/** \brief FaceUri schemes provided by this ProtocolFactory
*/
@@ -126,4 +170,18 @@
} // namespace nfd
+/** \brief registers a protocol factory
+ *
+ * This macro should appear once in .cpp of each protocol factory.
+ */
+#define NFD_REGISTER_PROTOCOL_FACTORY(PF) \
+static class NfdAuto ## PF ## ProtocolFactoryRegistrationClass \
+{ \
+public: \
+ NfdAuto ## PF ## ProtocolFactoryRegistrationClass() \
+ { \
+ ::nfd::face::ProtocolFactory::registerType<PF>(); \
+ } \
+} g_nfdAuto ## PF ## ProtocolFactoryRegistrationVariable
+
#endif // NFD_DAEMON_FACE_PROTOCOL_FACTORY_HPP
diff --git a/daemon/face/tcp-factory.cpp b/daemon/face/tcp-factory.cpp
index a3028b5..a273e42 100644
--- a/daemon/face/tcp-factory.cpp
+++ b/daemon/face/tcp-factory.cpp
@@ -32,6 +32,14 @@
namespace ip = boost::asio::ip;
NFD_LOG_INIT("TcpFactory");
+NFD_REGISTER_PROTOCOL_FACTORY(TcpFactory);
+
+const std::string&
+TcpFactory::getId()
+{
+ static std::string id("tcp");
+ return id;
+}
void
TcpFactory::processConfig(OptionalConfigSection configSection,
diff --git a/daemon/face/tcp-factory.hpp b/daemon/face/tcp-factory.hpp
index 161c490..059c8e1 100644
--- a/daemon/face/tcp-factory.hpp
+++ b/daemon/face/tcp-factory.hpp
@@ -37,6 +37,9 @@
class TcpFactory : public ProtocolFactory
{
public:
+ static const std::string&
+ getId();
+
/** \brief process face_system.tcp config section
*/
void
diff --git a/daemon/face/udp-factory.cpp b/daemon/face/udp-factory.cpp
index c9b4ac4..d4ee086 100644
--- a/daemon/face/udp-factory.cpp
+++ b/daemon/face/udp-factory.cpp
@@ -42,6 +42,15 @@
namespace ip = boost::asio::ip;
NFD_LOG_INIT("UdpFactory");
+NFD_REGISTER_PROTOCOL_FACTORY(UdpFactory);
+
+const std::string&
+UdpFactory::getId()
+{
+ static std::string id("udp");
+ return id;
+}
+
void
UdpFactory::processConfig(OptionalConfigSection configSection,
diff --git a/daemon/face/udp-factory.hpp b/daemon/face/udp-factory.hpp
index 7ec42b3..1e1a6ab 100644
--- a/daemon/face/udp-factory.hpp
+++ b/daemon/face/udp-factory.hpp
@@ -53,6 +53,9 @@
}
};
+ static const std::string&
+ getId();
+
/** \brief process face_system.udp config section
*/
void
diff --git a/daemon/face/unix-stream-factory.cpp b/daemon/face/unix-stream-factory.cpp
index b800b7c..19a39dc 100644
--- a/daemon/face/unix-stream-factory.cpp
+++ b/daemon/face/unix-stream-factory.cpp
@@ -32,6 +32,15 @@
namespace face {
NFD_LOG_INIT("UnixStreamFactory");
+NFD_REGISTER_PROTOCOL_FACTORY(UnixStreamFactory);
+
+const std::string&
+UnixStreamFactory::getId()
+{
+ static std::string id("unix");
+ return id;
+}
+
void
UnixStreamFactory::processConfig(OptionalConfigSection configSection,
diff --git a/daemon/face/unix-stream-factory.hpp b/daemon/face/unix-stream-factory.hpp
index 82d7e6e..43a6733 100644
--- a/daemon/face/unix-stream-factory.hpp
+++ b/daemon/face/unix-stream-factory.hpp
@@ -37,6 +37,9 @@
class UnixStreamFactory : public ProtocolFactory
{
public:
+ static const std::string&
+ getId();
+
/** \brief process face_system.unix config section
*/
void
diff --git a/daemon/face/websocket-factory.cpp b/daemon/face/websocket-factory.cpp
index 1898993..86ca95e 100644
--- a/daemon/face/websocket-factory.cpp
+++ b/daemon/face/websocket-factory.cpp
@@ -32,6 +32,15 @@
namespace ip = boost::asio::ip;
NFD_LOG_INIT("WebSocketFactory");
+NFD_REGISTER_PROTOCOL_FACTORY(WebSocketFactory);
+
+const std::string&
+WebSocketFactory::getId()
+{
+ static std::string id("websocket");
+ return id;
+}
+
void
WebSocketFactory::processConfig(OptionalConfigSection configSection,
diff --git a/daemon/face/websocket-factory.hpp b/daemon/face/websocket-factory.hpp
index a31c7cd..60f0a69 100644
--- a/daemon/face/websocket-factory.hpp
+++ b/daemon/face/websocket-factory.hpp
@@ -37,6 +37,9 @@
class WebSocketFactory : public ProtocolFactory
{
public:
+ static const std::string&
+ getId();
+
/** \brief process face_system.websocket config section
*/
void