mgmt: redesign ControlCommand and its usage in nfd::Controller

Split parameter encoding and validation into a separate class so that
different commands can have different formats in the future. Moreover,
request parameters and response parameters may be of different types.

Lastly, change the hierarchy to CRTP and make everything static. All the
encoding and validation details are the same for every request/response
of a given command type, so it makes no sense to allocate a separate
ControlCommand instance for each individual request.

Change-Id: I56c16dc3e275aaa48608478aad002d448c0492cc
diff --git a/tests/unit/mgmt/nfd/controller.t.cpp b/tests/unit/mgmt/nfd/controller.t.cpp
index 6760d5e..8be9d23 100644
--- a/tests/unit/mgmt/nfd/controller.t.cpp
+++ b/tests/unit/mgmt/nfd/controller.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2023 Regents of the University of California.
+ * Copyright (c) 2013-2025 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -20,6 +20,7 @@
  */
 
 #include "ndn-cxx/mgmt/nfd/controller.hpp"
+#include "ndn-cxx/mgmt/nfd/control-command.hpp"
 #include "ndn-cxx/mgmt/nfd/control-response.hpp"
 
 #include "tests/test-common.hpp"
@@ -87,8 +88,7 @@
   // 6 components: /localhost/nfd/faces/create/<parameters>/params-sha256=...
   BOOST_REQUIRE_EQUAL(requestInterest.getName().size(), 6);
   ControlParameters requestParams(requestInterest.getName()[4].blockFromValue());
-  FaceCreateCommand command;
-  BOOST_CHECK_NO_THROW(command.validateRequest(requestParams));
+  BOOST_CHECK_NO_THROW(FaceCreateCommand::validateRequest(requestParams));
   BOOST_CHECK_EQUAL(requestParams.getUri(), parameters.getUri());
 
   ControlParameters responseBody = makeFaceCreateResponse();
@@ -131,12 +131,11 @@
   this->advanceClocks(1_ms);
 
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
-  const Interest& requestInterest = face.sentInterests[0];
+  const Interest& request = face.sentInterests[0];
 
-  FaceCreateCommand command;
-  BOOST_CHECK(Name("/localhop/net/example/router1/nfd/rib/register").isPrefixOf(requestInterest.getName()));
-  BOOST_CHECK(requestInterest.isSigned());
-  BOOST_CHECK(requestInterest.isParametersDigestValid());
+  BOOST_CHECK(Name("/localhop/net/example/router1/nfd/rib/register").isPrefixOf(request.getName()));
+  BOOST_CHECK(request.isSigned());
+  BOOST_CHECK(request.isParametersDigestValid());
 }
 
 BOOST_AUTO_TEST_CASE(InvalidRequest)
@@ -146,7 +145,7 @@
   // Uri is missing
 
   BOOST_CHECK_THROW(controller.start<FaceCreateCommand>(parameters, succeedCallback, commandFailCallback),
-                    ControlCommand::ArgumentError);
+                    ArgumentError);
 }
 
 BOOST_AUTO_TEST_CASE(ValidationFailure)