mgmt: simplify StatusDataset implementation

Change-Id: Icf21cfd8bc47333461dadd06c347b723e77f5784
diff --git a/ndn-cxx/mgmt/control-response.cpp b/ndn-cxx/mgmt/control-response.cpp
index 5a18f48..cae2496 100644
--- a/ndn-cxx/mgmt/control-response.cpp
+++ b/ndn-cxx/mgmt/control-response.cpp
@@ -32,10 +32,7 @@
 static_assert(std::is_convertible_v<ControlResponse::Error*, tlv::Error*>,
               "ControlResponse::Error must inherit from tlv::Error");
 
-ControlResponse::ControlResponse()
-  : m_code(200)
-{
-}
+ControlResponse::ControlResponse() = default;
 
 ControlResponse::ControlResponse(uint32_t code, const std::string& text)
   : m_code(code)
@@ -48,16 +45,41 @@
   wireDecode(block);
 }
 
+ControlResponse&
+ControlResponse::setCode(uint32_t code)
+{
+  m_code = code;
+  m_wire.reset();
+  return *this;
+}
+
+ControlResponse&
+ControlResponse::setText(const std::string& text)
+{
+  m_text = text;
+  m_wire.reset();
+  return *this;
+}
+
+ControlResponse&
+ControlResponse::setBody(const Block& body)
+{
+  m_body = body;
+  m_body.encode(); // will do nothing if already encoded
+  m_wire.reset();
+  return *this;
+}
+
 const Block&
 ControlResponse::wireEncode() const
 {
-  if (m_wire.hasWire())
+  if (m_wire.hasWire()) {
     return m_wire;
+  }
 
   m_wire = Block(tlv::nfd::ControlResponse);
   m_wire.push_back(makeNonNegativeIntegerBlock(tlv::nfd::StatusCode, m_code));
   m_wire.push_back(makeStringBlock(tlv::nfd::StatusText, m_text));
-
   if (m_body.hasWire()) {
     m_wire.push_back(m_body);
   }
@@ -69,12 +91,12 @@
 void
 ControlResponse::wireDecode(const Block& wire)
 {
+  if (wire.type() != tlv::nfd::ControlResponse) {
+    NDN_THROW(Error("ControlResponse", wire.type()));
+  }
   m_wire = wire;
   m_wire.parse();
 
-  if (m_wire.type() != tlv::nfd::ControlResponse)
-    NDN_THROW(Error("ControlResponse", m_wire.type()));
-
   auto val = m_wire.elements_begin();
   if (val == m_wire.elements_end() || val->type() != tlv::nfd::StatusCode) {
     NDN_THROW(Error("missing StatusCode sub-element"));
@@ -94,12 +116,5 @@
     m_body = {};
 }
 
-std::ostream&
-operator<<(std::ostream& os, const ControlResponse& response)
-{
-  os << response.getCode() << " " << response.getText();
-  return os;
-}
-
 } // namespace mgmt
 } // namespace ndn
diff --git a/ndn-cxx/mgmt/control-response.hpp b/ndn-cxx/mgmt/control-response.hpp
index 99239fe..981384f8 100644
--- a/ndn-cxx/mgmt/control-response.hpp
+++ b/ndn-cxx/mgmt/control-response.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2021 Regents of the University of California.
+ * Copyright (c) 2013-2023 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -27,7 +27,8 @@
 namespace ndn {
 namespace mgmt {
 
-/** \brief ControlCommand response
+/**
+ * \brief ControlCommand response.
  */
 class ControlResponse
 {
@@ -46,19 +47,28 @@
   ControlResponse(const Block& block);
 
   uint32_t
-  getCode() const;
+  getCode() const
+  {
+    return m_code;
+  }
 
   ControlResponse&
   setCode(uint32_t code);
 
   const std::string&
-  getText() const;
+  getText() const
+  {
+    return m_text;
+  }
 
   ControlResponse&
   setText(const std::string& text);
 
   const Block&
-  getBody() const;
+  getBody() const
+  {
+    return m_body;
+  }
 
   ControlResponse&
   setBody(const Block& body);
@@ -70,59 +80,23 @@
   wireDecode(const Block& block);
 
 protected:
-  uint32_t m_code;
+  uint32_t m_code = 200;
   std::string m_text;
   Block m_body;
 
   mutable Block m_wire;
+
+private: // non-member operators
+  // NOTE: the following "hidden friend" operators are available via
+  //       argument-dependent lookup only and must be defined inline.
+
+  friend std::ostream&
+  operator<<(std::ostream& os, const ControlResponse& response)
+  {
+    return os << response.getCode() << ' ' << response.getText();
+  }
 };
 
-inline uint32_t
-ControlResponse::getCode() const
-{
-  return m_code;
-}
-
-inline ControlResponse&
-ControlResponse::setCode(uint32_t code)
-{
-  m_code = code;
-  m_wire.reset();
-  return *this;
-}
-
-inline const std::string&
-ControlResponse::getText() const
-{
-  return m_text;
-}
-
-inline ControlResponse&
-ControlResponse::setText(const std::string& text)
-{
-  m_text = text;
-  m_wire.reset();
-  return *this;
-}
-
-inline const Block&
-ControlResponse::getBody() const
-{
-  return m_body;
-}
-
-inline ControlResponse&
-ControlResponse::setBody(const Block& body)
-{
-  m_body = body;
-  m_body.encode(); // will do nothing if already encoded
-  m_wire.reset();
-  return *this;
-}
-
-std::ostream&
-operator<<(std::ostream& os, const ControlResponse& response);
-
 } // namespace mgmt
 } // namespace ndn
 
diff --git a/ndn-cxx/mgmt/nfd/controller.cpp b/ndn-cxx/mgmt/nfd/controller.cpp
index 9698ada..38a0052 100644
--- a/ndn-cxx/mgmt/nfd/controller.cpp
+++ b/ndn-cxx/mgmt/nfd/controller.cpp
@@ -79,8 +79,8 @@
                                    const CommandFailureCallback& onFailure)
 {
   m_validator.validate(data,
-    [=] (const Data& data) {
-      processValidatedCommandResponse(data, command, onSuccess, onFailure);
+    [=] (const Data& d) {
+      processValidatedCommandResponse(d, command, onSuccess, onFailure);
     },
     [=] (const Data&, const auto& error) {
       if (onFailure)
@@ -101,12 +101,11 @@
   }
   catch (const tlv::Error& e) {
     if (onFailure)
-      onFailure(ControlResponse(ERROR_SERVER, e.what()));
+      onFailure(ControlResponse(ERROR_SERVER, "ControlResponse decoding failure: "s + e.what()));
     return;
   }
 
-  uint32_t code = response.getCode();
-  if (code >= ERROR_LBOUND) {
+  if (response.getCode() >= ERROR_LBOUND) {
     if (onFailure)
       onFailure(response);
     return;
@@ -118,7 +117,7 @@
   }
   catch (const tlv::Error& e) {
     if (onFailure)
-      onFailure(ControlResponse(ERROR_SERVER, e.what()));
+      onFailure(ControlResponse(ERROR_SERVER, "ControlParameters decoding failure: "s + e.what()));
     return;
   }
 
@@ -127,7 +126,7 @@
   }
   catch (const ControlCommand::ArgumentError& e) {
     if (onFailure)
-      onFailure(ControlResponse(ERROR_SERVER, e.what()));
+      onFailure(ControlResponse(ERROR_SERVER, "Invalid response: "s + e.what()));
     return;
   }
 
@@ -145,11 +144,9 @@
   fetcherOptions.maxTimeout = options.getTimeout();
 
   auto fetcher = SegmentFetcher::start(m_face, Interest(prefix), m_validator, fetcherOptions);
-  if (processResponse) {
-    fetcher->onComplete.connect(processResponse);
-  }
+  fetcher->onComplete.connect(processResponse);
   if (onFailure) {
-    fetcher->onError.connect([=] (uint32_t code, const std::string& msg) {
+    fetcher->onError.connect([onFailure] (uint32_t code, const std::string& msg) {
       processDatasetFetchError(onFailure, code, msg);
     });
   }
diff --git a/ndn-cxx/mgmt/nfd/controller.hpp b/ndn-cxx/mgmt/nfd/controller.hpp
index 3671182..d0d3f08 100644
--- a/ndn-cxx/mgmt/nfd/controller.hpp
+++ b/ndn-cxx/mgmt/nfd/controller.hpp
@@ -28,7 +28,6 @@
 #include "ndn-cxx/security/interest-signer.hpp"
 #include "ndn-cxx/security/key-chain.hpp"
 #include "ndn-cxx/security/validator-null.hpp"
-#include "ndn-cxx/security/validator.hpp"
 #include "ndn-cxx/util/segment-fetcher.hpp"
 
 namespace ndn {
@@ -39,11 +38,37 @@
 
 /**
  * \defgroup management Management
- * \brief Classes and data structures to manage NDN forwarder.
+ * \brief Classes and data structures to manage an NDN forwarder.
  */
 
 /**
  * \ingroup management
+ * \brief Callback on command success.
+ */
+using CommandSuccessCallback = std::function<void(const ControlParameters&)>;
+
+/**
+ * \ingroup management
+ * \brief Callback on command failure.
+ */
+using CommandFailureCallback = std::function<void(const ControlResponse&)>;
+
+/**
+ * \ingroup management
+ * \brief Callback on dataset retrieval success.
+ */
+template<typename Dataset>
+using DatasetSuccessCallback =
+  std::function<void(const std::invoke_result_t<decltype(&Dataset::parseResult), Dataset, ConstBufferPtr>&)>;
+
+/**
+ * \ingroup management
+ * \brief Callback on dataset retrieval failure.
+ */
+using DatasetFailureCallback = std::function<void(uint32_t code, const std::string& reason)>;
+
+/**
+ * \ingroup management
  * \brief NFD Management protocol client.
  * \sa https://redmine.named-data.net/projects/nfd/wiki/Management
  */
@@ -51,30 +76,6 @@
 {
 public:
   /**
-   * \brief Callback on command success.
-   */
-  using CommandSuccessCallback = std::function<void(const ControlParameters&)>;
-
-  /**
-   * \brief Callback on command failure.
-   */
-  using CommandFailureCallback = std::function<void(const ControlResponse&)>;
-  using CommandFailCallback = CommandFailureCallback; // backward compat
-
-  /**
-   * \brief Callback on dataset retrieval success.
-   */
-  template<typename Dataset>
-  using DatasetSuccessCallback = std::function<void(const std::invoke_result_t<decltype(&Dataset::parseResult),
-                                                                               Dataset, ConstBufferPtr>&)>;
-
-  /**
-   * \brief Callback on dataset retrieval failure.
-   */
-  using DatasetFailureCallback = std::function<void(uint32_t code, const std::string& reason)>;
-  using DatasetFailCallback = DatasetFailureCallback; // backward compat
-
-  /**
    * \brief Construct a Controller that uses \p face as transport and \p keyChain to sign commands.
    */
   Controller(Face& face, KeyChain& keyChain,
@@ -90,7 +91,7 @@
   start(const ControlParameters& parameters,
         const CommandSuccessCallback& onSuccess,
         const CommandFailureCallback& onFailure,
-        const CommandOptions& options = CommandOptions())
+        const CommandOptions& options = {})
   {
     startCommand(std::make_shared<Command>(), parameters, onSuccess, onFailure, options);
   }
@@ -102,9 +103,9 @@
   std::enable_if_t<std::is_default_constructible_v<Dataset>>
   fetch(const DatasetSuccessCallback<Dataset>& onSuccess,
         const DatasetFailureCallback& onFailure,
-        const CommandOptions& options = CommandOptions())
+        const CommandOptions& options = {})
   {
-    fetchDataset(std::make_shared<Dataset>(), onSuccess, onFailure, options);
+    fetchDataset(Dataset(), onSuccess, onFailure, options);
   }
 
   /**
@@ -115,10 +116,9 @@
   fetch(ParamType&& param,
         const DatasetSuccessCallback<Dataset>& onSuccess,
         const DatasetFailureCallback& onFailure,
-        const CommandOptions& options = CommandOptions())
+        const CommandOptions& options = {})
   {
-    fetchDataset(std::make_shared<Dataset>(std::forward<ParamType>(param)),
-                 onSuccess, onFailure, options);
+    fetchDataset(Dataset(std::forward<ParamType>(param)), onSuccess, onFailure, options);
   }
 
 private:
@@ -135,7 +135,7 @@
                          const CommandSuccessCallback& onSuccess,
                          const CommandFailureCallback& onFailure);
 
-  void
+  static void
   processValidatedCommandResponse(const Data& data,
                                   const shared_ptr<ControlCommand>& command,
                                   const CommandSuccessCallback& onSuccess,
@@ -143,7 +143,7 @@
 
   template<typename Dataset>
   void
-  fetchDataset(shared_ptr<Dataset> dataset,
+  fetchDataset(Dataset&& dataset,
                const DatasetSuccessCallback<Dataset>& onSuccess,
                const DatasetFailureCallback& onFailure,
                const CommandOptions& options);
@@ -154,24 +154,22 @@
                const DatasetFailureCallback& onFailure,
                const CommandOptions& options);
 
-  void
+  static void
   processDatasetFetchError(const DatasetFailureCallback& onFailure, uint32_t code, std::string msg);
 
 public:
-  /// Error code for timeout.
-  static constexpr uint32_t ERROR_TIMEOUT = 10060;
-
-  /// Error code for network %Nack.
-  static constexpr uint32_t ERROR_NACK = 10800;
-
-  /// Error code for response validation failure.
-  static constexpr uint32_t ERROR_VALIDATION = 10021;
-
-  /// Error code for server error.
-  static constexpr uint32_t ERROR_SERVER = 500;
-
-  /// Inclusive lower bound of error codes.
-  static constexpr uint32_t ERROR_LBOUND = 400;
+  enum : uint32_t {
+    /// Inclusive lower bound of error codes.
+    ERROR_LBOUND = 400,
+    /// Error code for server error.
+    ERROR_SERVER = 500,
+    /// Error code for timeout.
+    ERROR_TIMEOUT = 10060,
+    /// Error code for network %Nack.
+    ERROR_NACK = 10800,
+    /// Error code for response validation failure.
+    ERROR_VALIDATION = 10021,
+  };
 
 protected:
   Face& m_face;
@@ -185,21 +183,21 @@
 
 template<typename Dataset>
 void
-Controller::fetchDataset(shared_ptr<Dataset> dataset,
+Controller::fetchDataset(Dataset&& dataset,
                          const DatasetSuccessCallback<Dataset>& onSuccess,
                          const DatasetFailureCallback& onFailure,
                          const CommandOptions& options)
 {
-  Name prefix = dataset->getDatasetPrefix(options.getPrefix());
+  Name prefix = dataset.getDatasetPrefix(options.getPrefix());
   fetchDataset(prefix,
-    [dataset = std::move(dataset), onSuccess, onFailure] (ConstBufferPtr payload) {
+    [=, dataset = std::forward<Dataset>(dataset)] (ConstBufferPtr payload) {
       std::invoke_result_t<decltype(&Dataset::parseResult), Dataset, ConstBufferPtr> result;
       try {
-        result = dataset->parseResult(std::move(payload));
+        result = dataset.parseResult(std::move(payload));
       }
       catch (const tlv::Error& e) {
         if (onFailure)
-          onFailure(ERROR_SERVER, e.what());
+          onFailure(ERROR_SERVER, "Dataset decoding failure: "s + e.what());
         return;
       }
       if (onSuccess)
diff --git a/ndn-cxx/mgmt/nfd/face-event-notification.cpp b/ndn-cxx/mgmt/nfd/face-event-notification.cpp
index 09a7e05..517064a 100644
--- a/ndn-cxx/mgmt/nfd/face-event-notification.cpp
+++ b/ndn-cxx/mgmt/nfd/face-event-notification.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2019 Regents of the University of California.
+ * Copyright (c) 2013-2023 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -31,10 +31,7 @@
 
 BOOST_CONCEPT_ASSERT((NotificationStreamItem<FaceEventNotification>));
 
-FaceEventNotification::FaceEventNotification()
-  : m_kind(FACE_EVENT_NONE)
-{
-}
+FaceEventNotification::FaceEventNotification() = default;
 
 FaceEventNotification::FaceEventNotification(const Block& block)
 {
diff --git a/ndn-cxx/mgmt/nfd/face-event-notification.hpp b/ndn-cxx/mgmt/nfd/face-event-notification.hpp
index df6909b..959cf93 100644
--- a/ndn-cxx/mgmt/nfd/face-event-notification.hpp
+++ b/ndn-cxx/mgmt/nfd/face-event-notification.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2022 Regents of the University of California.
+ * Copyright (c) 2013-2023 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -67,7 +67,7 @@
   setKind(FaceEventKind kind);
 
 private:
-  FaceEventKind m_kind;
+  FaceEventKind m_kind = FACE_EVENT_NONE;
 };
 
 NDN_CXX_DECLARE_WIRE_ENCODE_INSTANTIATIONS(FaceEventNotification);
diff --git a/ndn-cxx/mgmt/nfd/face-monitor.cpp b/ndn-cxx/mgmt/nfd/face-monitor.cpp
deleted file mode 100644
index b86802b..0000000
--- a/ndn-cxx/mgmt/nfd/face-monitor.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/*
- * Copyright (c) 2014-2018 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 ndn-cxx library (NDN C++ library with eXperimental eXtensions).
- *
- * ndn-cxx library is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later version.
- *
- * ndn-cxx library 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 Lesser General Public License for more details.
- *
- * You should have received copies of the GNU General Public License and GNU Lesser
- * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
- */
-
-#include "ndn-cxx/mgmt/nfd/face-monitor.hpp"
-
-namespace ndn {
-namespace nfd {
-
-FaceMonitor::FaceMonitor(Face& face)
-  : NotificationSubscriber<FaceEventNotification>(face, "ndn:/localhost/nfd/faces/events")
-{
-}
-
-} // namespace nfd
-} // namespace ndn
diff --git a/ndn-cxx/mgmt/nfd/face-monitor.hpp b/ndn-cxx/mgmt/nfd/face-monitor.hpp
index bb32ee2..07fefd8 100644
--- a/ndn-cxx/mgmt/nfd/face-monitor.hpp
+++ b/ndn-cxx/mgmt/nfd/face-monitor.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2021 Regents of the University of California,
+ * Copyright (c) 2014-2023 Regents of the University of California,
  *                         Arizona Board of Regents,
  *                         Colorado State University,
  *                         University Pierre & Marie Curie, Sorbonne University,
@@ -34,14 +34,18 @@
 namespace ndn {
 namespace nfd {
 
-/** \brief A subscriber for Face status change notification stream
- *  \sa https://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Face-Status-Change-Notification
+/**
+ * \brief A subscriber for the %Face status change notification stream.
+ * \sa https://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Face-Status-Change-Notification
  */
 class FaceMonitor : public util::NotificationSubscriber<FaceEventNotification>
 {
 public:
   explicit
-  FaceMonitor(Face& face);
+  FaceMonitor(Face& face)
+    : NotificationSubscriber(face, "/localhost/nfd/faces/events")
+  {
+  }
 };
 
 } // namespace nfd
diff --git a/ndn-cxx/mgmt/nfd/status-dataset.cpp b/ndn-cxx/mgmt/nfd/status-dataset.cpp
index f9d801a..fe8b178 100644
--- a/ndn-cxx/mgmt/nfd/status-dataset.cpp
+++ b/ndn-cxx/mgmt/nfd/status-dataset.cpp
@@ -20,63 +20,50 @@
  */
 
 #include "ndn-cxx/mgmt/nfd/status-dataset.hpp"
-#include "ndn-cxx/util/concepts.hpp"
 
 namespace ndn {
 namespace nfd {
 
-StatusDataset::StatusDataset(const PartialName& datasetName)
-  : m_datasetName(datasetName)
-{
-}
-
-StatusDataset::~StatusDataset() = default;
-
 Name
-StatusDataset::getDatasetPrefix(const Name& prefix) const
+StatusDatasetBase::getDatasetPrefix(const Name& prefix) const
 {
-  Name name;
-  name.append(prefix).append(m_datasetName);
-  this->addParameters(name);
-  return name;
-}
-
-void
-StatusDataset::addParameters(Name&) const
-{
+  return Name(prefix).append(m_datasetName);
 }
 
 /**
  * \brief Parses elements into a vector of T.
  * \tparam T element type
  * \param payload pointer to a buffer of zero or more blocks of decodable by T
- * \return a vector of T
- * \throw tlv::Error cannot parse payload
+ * \throw StatusDatasetParseError cannot parse payload
  */
 template<typename T>
 static std::vector<T>
 parseDatasetVector(const ConstBufferPtr& payload)
 {
-  BOOST_CONCEPT_ASSERT((WireDecodable<T>));
-
   std::vector<T> result;
 
   size_t offset = 0;
   while (offset < payload->size()) {
     auto [isOk, block] = Block::fromBuffer(payload, offset);
     if (!isOk) {
-      NDN_THROW(StatusDataset::ParseResultError("cannot decode Block"));
+      NDN_THROW(StatusDatasetParseError("Cannot decode a valid TLV at offset " + std::to_string(offset)));
+    }
+
+    try {
+      result.emplace_back(block);
+    }
+    catch (const tlv::Error& e) {
+      NDN_THROW_NESTED(StatusDatasetParseError(e.what()));
     }
 
     offset += block.size();
-    result.emplace_back(block);
   }
 
   return result;
 }
 
 ForwarderGeneralStatusDataset::ForwarderGeneralStatusDataset()
-  : StatusDataset("status/general")
+  : StatusDatasetBase("status/general")
 {
 }
 
@@ -86,36 +73,38 @@
   return ForwarderStatus(Block(tlv::Content, std::move(payload)));
 }
 
-FaceDatasetBase::FaceDatasetBase(const PartialName& datasetName)
-  : StatusDataset(datasetName)
+FaceDataset::FaceDataset()
+  : StatusDatasetBase("faces/list")
 {
 }
 
 std::vector<FaceStatus>
-FaceDatasetBase::parseResult(ConstBufferPtr payload) const
+FaceDataset::parseResult(ConstBufferPtr payload) const
 {
   return parseDatasetVector<FaceStatus>(payload);
 }
 
-FaceDataset::FaceDataset()
-  : FaceDatasetBase("faces/list")
-{
-}
-
 FaceQueryDataset::FaceQueryDataset(const FaceQueryFilter& filter)
-  : FaceDatasetBase("faces/query")
+  : StatusDatasetBase("faces/query")
   , m_filter(filter)
 {
 }
 
-void
-FaceQueryDataset::addParameters(Name& name) const
+Name
+FaceQueryDataset::getDatasetPrefix(const Name& prefix) const
 {
-  name.append(m_filter.wireEncode());
+  return StatusDatasetBase::getDatasetPrefix(prefix)
+         .append(m_filter.wireEncode());
+}
+
+std::vector<FaceStatus>
+FaceQueryDataset::parseResult(ConstBufferPtr payload) const
+{
+  return parseDatasetVector<FaceStatus>(payload);
 }
 
 ChannelDataset::ChannelDataset()
-  : StatusDataset("faces/channels")
+  : StatusDatasetBase("faces/channels")
 {
 }
 
@@ -126,7 +115,7 @@
 }
 
 FibDataset::FibDataset()
-  : StatusDataset("fib/list")
+  : StatusDatasetBase("fib/list")
 {
 }
 
@@ -137,7 +126,7 @@
 }
 
 CsInfoDataset::CsInfoDataset()
-  : StatusDataset("cs/info")
+  : StatusDatasetBase("cs/info")
 {
 }
 
@@ -148,7 +137,7 @@
 }
 
 StrategyChoiceDataset::StrategyChoiceDataset()
-  : StatusDataset("strategy-choice/list")
+  : StatusDatasetBase("strategy-choice/list")
 {
 }
 
@@ -159,7 +148,7 @@
 }
 
 RibDataset::RibDataset()
-  : StatusDataset("rib/list")
+  : StatusDatasetBase("rib/list")
 {
 }
 
diff --git a/ndn-cxx/mgmt/nfd/status-dataset.hpp b/ndn-cxx/mgmt/nfd/status-dataset.hpp
index 4fd0681..93cc331 100644
--- a/ndn-cxx/mgmt/nfd/status-dataset.hpp
+++ b/ndn-cxx/mgmt/nfd/status-dataset.hpp
@@ -36,16 +36,23 @@
 namespace nfd {
 
 /**
- * \ingroup management
- * \brief Base class of NFD `%StatusDataset`.
- * \sa https://redmine.named-data.net/projects/nfd/wiki/StatusDataset
+ * \brief Exception raised when the fetched payload cannot be parsed as a StatusDataset.
+ * \sa StatusDatasetBase::parseResult()
  */
-class StatusDataset : noncopyable
+class StatusDatasetParseError : public tlv::Error
 {
 public:
-  virtual
-  ~StatusDataset();
+  using tlv::Error::Error;
+};
 
+/**
+ * \ingroup management
+ * \brief Base class of NFD `StatusDataset`.
+ * \sa https://redmine.named-data.net/projects/nfd/wiki/StatusDataset
+ */
+class StatusDatasetBase
+{
+public:
   /**
    * \brief Constructs a name prefix for the dataset.
    * \param prefix Top-level prefix, such as `/localhost/nfd`.
@@ -54,22 +61,13 @@
   Name
   getDatasetPrefix(const Name& prefix) const;
 
-  /**
-   * \brief Indicates the reassembled payload cannot be parsed successfully.
-   * \sa parseResult()
-   */
-  class ParseResultError : public tlv::Error
-  {
-  public:
-    using tlv::Error::Error;
-  };
-
 #ifdef DOXYGEN
   /**
    * \brief Parses a result from a reassembled payload.
    * \param payload The reassembled payload.
    * \return The parsed result, usually a vector.
    * \throw tlv::Error Cannot parse the payload.
+   * \sa StatusDatasetParseError
    */
   ResultType
   parseResult(ConstBufferPtr payload) const;
@@ -77,30 +75,29 @@
 
 protected:
   /**
-   * \brief Constructs a StatusDataset instance with the given sub-prefix.
+   * \brief Protected constructor.
    * \param datasetName Dataset name after top-level prefix, such as `faces/list`.
    */
   explicit
-  StatusDataset(const PartialName& datasetName);
+  StatusDatasetBase(PartialName datasetName)
+    : m_datasetName(std::move(datasetName))
+  {
+  }
 
-private:
-  /**
-   * \brief Appends parameters to the dataset name prefix.
-   * \param[in,out] name The dataset name prefix onto which parameter components can be appended.
-   */
-  virtual void
-  addParameters(Name& name) const;
+  ~StatusDatasetBase() = default;
 
-private:
+protected:
   PartialName m_datasetName;
 };
 
+using StatusDataset [[deprecated("use StatusDatasetBase")]] = StatusDatasetBase;
+
 /**
  * \ingroup management
  * \brief Represents a `status/general` dataset.
  * \sa https://redmine.named-data.net/projects/nfd/wiki/ForwarderStatus#General-Status-Dataset
  */
-class ForwarderGeneralStatusDataset : public StatusDataset
+class ForwarderGeneralStatusDataset : public StatusDatasetBase
 {
 public:
   ForwarderGeneralStatusDataset();
@@ -111,28 +108,16 @@
 
 /**
  * \ingroup management
- * \brief Provides common functionality among FaceDataset and FaceQueryDataset.
- */
-class FaceDatasetBase : public StatusDataset
-{
-public:
-  std::vector<FaceStatus>
-  parseResult(ConstBufferPtr payload) const;
-
-protected:
-  explicit
-  FaceDatasetBase(const PartialName& datasetName);
-};
-
-/**
- * \ingroup management
  * \brief Represents a `faces/list` dataset.
  * \sa https://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Face-Dataset
  */
-class FaceDataset : public FaceDatasetBase
+class FaceDataset : public StatusDatasetBase
 {
 public:
   FaceDataset();
+
+  std::vector<FaceStatus>
+  parseResult(ConstBufferPtr payload) const;
 };
 
 /**
@@ -140,15 +125,17 @@
  * \brief Represents a `faces/query` dataset.
  * \sa https://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Query-Operation
  */
-class FaceQueryDataset : public FaceDatasetBase
+class FaceQueryDataset : public StatusDatasetBase
 {
 public:
   explicit
   FaceQueryDataset(const FaceQueryFilter& filter);
 
-private:
-  void
-  addParameters(Name& name) const override;
+  Name
+  getDatasetPrefix(const Name& prefix) const;
+
+  std::vector<FaceStatus>
+  parseResult(ConstBufferPtr payload) const;
 
 private:
   FaceQueryFilter m_filter;
@@ -159,7 +146,7 @@
  * \brief Represents a `faces/channels` dataset.
  * \sa https://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#Channel-Dataset
  */
-class ChannelDataset : public StatusDataset
+class ChannelDataset : public StatusDatasetBase
 {
 public:
   ChannelDataset();
@@ -173,7 +160,7 @@
  * \brief Represents a `fib/list` dataset.
  * \sa https://redmine.named-data.net/projects/nfd/wiki/FibMgmt#FIB-Dataset
  */
-class FibDataset : public StatusDataset
+class FibDataset : public StatusDatasetBase
 {
 public:
   FibDataset();
@@ -187,7 +174,7 @@
  * \brief Represents a `cs/info` dataset.
  * \sa https://redmine.named-data.net/projects/nfd/wiki/CsMgmt#CS-Information-Dataset
  */
-class CsInfoDataset : public StatusDataset
+class CsInfoDataset : public StatusDatasetBase
 {
 public:
   CsInfoDataset();
@@ -201,7 +188,7 @@
  * \brief Represents a `strategy-choice/list` dataset.
  * \sa https://redmine.named-data.net/projects/nfd/wiki/StrategyChoice#Strategy-Choice-Dataset
  */
-class StrategyChoiceDataset : public StatusDataset
+class StrategyChoiceDataset : public StatusDatasetBase
 {
 public:
   StrategyChoiceDataset();
@@ -215,7 +202,7 @@
  * \brief Represents a `rib/list` dataset.
  * \sa https://redmine.named-data.net/projects/nfd/wiki/RibMgmt#RIB-Dataset
  */
-class RibDataset : public StatusDataset
+class RibDataset : public StatusDatasetBase
 {
 public:
   RibDataset();
diff --git a/tests/unit/mgmt/nfd/controller-fixture.hpp b/tests/unit/mgmt/nfd/controller-fixture.hpp
index 8afee13..38b44fd 100644
--- a/tests/unit/mgmt/nfd/controller-fixture.hpp
+++ b/tests/unit/mgmt/nfd/controller-fixture.hpp
@@ -62,8 +62,8 @@
   ndn::util::DummyClientFace face;
   DummyValidator m_validator;
   Controller controller;
-  Controller::CommandFailureCallback commandFailCallback;
-  Controller::DatasetFailureCallback datasetFailCallback;
+  CommandFailureCallback commandFailCallback;
+  DatasetFailureCallback datasetFailCallback;
   std::vector<uint32_t> failCodes;
 };
 
diff --git a/tests/unit/mgmt/nfd/controller.t.cpp b/tests/unit/mgmt/nfd/controller.t.cpp
index f7f4a9a..d68611f 100644
--- a/tests/unit/mgmt/nfd/controller.t.cpp
+++ b/tests/unit/mgmt/nfd/controller.t.cpp
@@ -47,7 +47,7 @@
   }
 
 protected:
-  Controller::CommandSuccessCallback succeedCallback = [this] (const auto& params) {
+  CommandSuccessCallback succeedCallback = [this] (const auto& params) {
     succeeds.push_back(params);
   };
   std::vector<ControlParameters> succeeds;
diff --git a/tests/unit/mgmt/nfd/status-dataset.t.cpp b/tests/unit/mgmt/nfd/status-dataset.t.cpp
index 633a45d..511498d 100644
--- a/tests/unit/mgmt/nfd/status-dataset.t.cpp
+++ b/tests/unit/mgmt/nfd/status-dataset.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2022 Regents of the University of California.
+ * Copyright (c) 2013-2023 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -46,8 +46,6 @@
   void
   sendDataset(const Name& prefix, const T& payload)
   {
-    BOOST_CONCEPT_ASSERT((WireEncodable<T>));
-
     auto data = this->prepareDatasetReply(prefix);
     data->setContent(payload.wireEncode());
     face.receive(*signData(data));
@@ -67,9 +65,6 @@
     // because this test suite focuses on Controller::fetch<StatusDataset>,
     // and is not intended to cover SegmentFetcher behavior.
 
-    BOOST_CONCEPT_ASSERT((WireEncodable<T1>));
-    BOOST_CONCEPT_ASSERT((WireEncodable<T2>));
-
     EncodingBuffer buffer;
     payload2.wireEncode(buffer);
     payload1.wireEncode(buffer);
@@ -80,7 +75,7 @@
   }
 
 private:
-  shared_ptr<Data>
+  std::shared_ptr<Data>
   prepareDatasetReply(const Name& prefix)
   {
     Name name = prefix;
@@ -97,7 +92,7 @@
                          " cannot be satisfied by this Data " << name);
     }
 
-    auto data = make_shared<Data>(name);
+    auto data = std::make_shared<Data>(name);
     data->setFreshnessPeriod(1_s);
     data->setFinalBlock(name[-1]);
     return data;