Register procedures

For a procedure to be available to run, it needs to have
a call to a REGISTER_PROCEDURE macro.  This macro wraps
a call to a Register class that adds the procedure to the list
along with the name to call it.

Change-Id: I20d02e8f004c1c726228469465ae89b60ee52d66
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 57c0e2c..6f785bc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,6 +7,7 @@
 	proc_control.cpp \
 	cfam_access.cpp \
 	filedescriptor.cpp \
+	registration.cpp \
 	targeting.cpp \
 	procedures/p9/start_host.cpp \
 	procedures/p9/vcs_workaround.cpp
diff --git a/p9_procedures.hpp b/p9_procedures.hpp
deleted file mode 100644
index 2e7ff1a..0000000
--- a/p9_procedures.hpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-namespace openpower
-{
-namespace p9
-{
-
-/**
- * @brief Starts the self boot engine on P9 position 0 to kick off a boot.
- * @return void
- */
-void startHost();
-
-
-/**
- * @brief Performs the 'VCS Workaround' on all P9s in the system.
- * @return void
- */
-void vcsWorkaround();
-
-}
-}
diff --git a/proc_control.cpp b/proc_control.cpp
index 8f6f86c..10c3ffa 100644
--- a/proc_control.cpp
+++ b/proc_control.cpp
@@ -17,51 +17,44 @@
 #include <functional>
 #include <iostream>
 #include <phosphor-logging/log.hpp>
-#include "p9_procedures.hpp"
+#include "registration.hpp"
 
-constexpr auto procedures =
-{
-    std::make_tuple("startHost", openpower::p9::startHost),
-    std::make_tuple("vcsWorkaround", openpower::p9::vcsWorkaround)
-};
+using namespace openpower::util;
 
-void usage(char** argv)
+void usage(char** argv, const ProcedureMap& procedures)
 {
     std::cerr << "Usage: " << argv[0] << " [action]\n";
     std::cerr << "   actions:\n";
 
     for (const auto& p : procedures)
     {
-        std::cerr << "     " << std::get<0>(p) << "\n";
+        std::cerr << "     " << p.first << "\n";
     }
 }
 
 int main(int argc, char** argv)
 {
+    const ProcedureMap& procedures = Registration::getProcedures();
+
     if (argc != 2)
     {
-        usage(argv);
+        usage(argv, procedures);
         return -1;
     }
 
     std::string action{argv[1]};
 
-    auto finder = [&action](const auto& p)
-    {
-        return std::get<0>(p) == action;
-    };
-    auto procedure = std::find_if(procedures.begin(), procedures.end(), finder);
+    auto procedure = procedures.find(action);
 
     if (procedure == procedures.end())
     {
-        usage(argv);
+        usage(argv, procedures);
         return -1;
     }
 
-    auto function = std::get<1>(*procedure);
     try
     {
-        function();
+        procedure->second();
     }
     catch (std::exception& e)
     {
diff --git a/procedures/p9/start_host.cpp b/procedures/p9/start_host.cpp
index f781e68..a31503b 100644
--- a/procedures/p9/start_host.cpp
+++ b/procedures/p9/start_host.cpp
@@ -16,6 +16,7 @@
 #include <phosphor-logging/log.hpp>
 #include "cfam_access.hpp"
 #include "p9_cfam.hpp"
+#include "registration.hpp"
 #include "targeting.hpp"
 
 namespace openpower
@@ -71,6 +72,7 @@
     writeRegWithMask(master, P9_CBS_CS, 0x80000000, 0x80000000);
 }
 
+REGISTER_PROCEDURE("startHost", startHost);
 
 }
 }
diff --git a/procedures/p9/vcs_workaround.cpp b/procedures/p9/vcs_workaround.cpp
index 64b41c1..07e7fd0 100644
--- a/procedures/p9/vcs_workaround.cpp
+++ b/procedures/p9/vcs_workaround.cpp
@@ -16,6 +16,7 @@
 #include <phosphor-logging/log.hpp>
 #include "cfam_access.hpp"
 #include "p9_cfam.hpp"
+#include "registration.hpp"
 #include "targeting.hpp"
 
 namespace openpower
@@ -60,5 +61,7 @@
     }
 }
 
+REGISTER_PROCEDURE("vcsWorkaround", vcsWorkaround);
+
 }
 }
diff --git a/registration.cpp b/registration.cpp
new file mode 100644
index 0000000..f45f767
--- /dev/null
+++ b/registration.cpp
@@ -0,0 +1,20 @@
+/**
+ * Copyright © 2017 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "registration.hpp"
+
+using namespace openpower::util;
+ProcedureMap Registration::procedures{};
+
diff --git a/registration.hpp b/registration.hpp
new file mode 100644
index 0000000..d0dde00
--- /dev/null
+++ b/registration.hpp
@@ -0,0 +1,64 @@
+#pragma once
+
+#include <functional>
+#include <map>
+#include <string>
+#include <iostream>
+
+namespace openpower
+{
+namespace util
+{
+
+using ProcedureName = std::string;
+using ProcedureFunction = std::function<void()>;
+using ProcedureMap = std::map<ProcedureName, ProcedureFunction>;
+
+/**
+ * This macro can be used in each procedure cpp file to make it
+ * available to the openpower-proc-control executable.
+ */
+#define REGISTER_PROCEDURE(name, func) \
+  namespace func##_ns \
+  { \
+    openpower::util::Registration r{ \
+        std::move(name), std::move(func)}; \
+  }
+
+
+/**
+ * Used to register procedures.  Each procedure function can then
+ * be found in a map via its name.
+ */
+class Registration
+{
+    public:
+
+        /**
+         *  Adds the procedure name and function to the internal
+         *  procedure map.
+         *
+         *  @param[in] name - the procedure name
+         *  @param[in] function - the function to run
+         */
+        Registration(ProcedureName&& name,
+                     ProcedureFunction&& function)
+        {
+            procedures.emplace(std::move(name), std::move(function));
+        }
+
+        /**
+         * Returns the map of procedures
+         */
+        static const ProcedureMap& getProcedures()
+        {
+            return procedures;
+        }
+
+    private:
+
+        static ProcedureMap procedures;
+};
+
+}
+}
diff --git a/test/Makefile.am b/test/Makefile.am
index ce78754..bf86e6a 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -13,4 +13,4 @@
 	$(PHOSPHOR_LOGGING_LIBS) -lstdc++fs
 
 utest_SOURCES = utest.cpp
-utest_LDADD = $(top_srcdir)/targeting.cpp
+utest_LDADD = $(top_srcdir)/targeting.cpp $(top_srcdir)/registration.cpp
diff --git a/test/utest.cpp b/test/utest.cpp
index d91004d..0d3c2c9 100644
--- a/test/utest.cpp
+++ b/test/utest.cpp
@@ -17,8 +17,10 @@
 #include <experimental/filesystem>
 #include <fstream>
 #include <stdlib.h>
+#include "registration.hpp"
 #include "targeting.hpp"
 
+using namespace openpower::util;
 using namespace openpower::targeting;
 namespace fs = std::experimental::filesystem;
 
@@ -98,3 +100,31 @@
         }
     }
 }
+
+
+void func1()
+{
+    std::cout << "Hello\n";
+}
+
+void func2()
+{
+    std::cout << "World\n";
+}
+
+REGISTER_PROCEDURE("hello", func1);
+REGISTER_PROCEDURE("world", func2);
+
+
+TEST(RegistrationTest, TestReg)
+{
+    int count = 0;
+    for (const auto& p : Registration::getProcedures())
+    {
+        std::cout << p.first << std::endl;
+        p.second();
+        count++;
+    }
+
+    ASSERT_EQ(count, 2);
+}