From f67e319ab9c7e39ef794a7dd925b697a93ae4256 Mon Sep 17 00:00:00 2001
From: Nicolas Pope <nwpope@utu.fi>
Date: Mon, 3 Jun 2019 07:42:56 +0300
Subject: [PATCH] Add events to configuable, untested

---
 components/common/cpp/CMakeLists.txt          |  1 +
 .../common/cpp/include/ftl/configurable.hpp   | 64 ++++++++++++++++++-
 components/common/cpp/src/configurable.cpp    | 24 +++++++
 3 files changed, 88 insertions(+), 1 deletion(-)
 create mode 100644 components/common/cpp/src/configurable.cpp

diff --git a/components/common/cpp/CMakeLists.txt b/components/common/cpp/CMakeLists.txt
index 2b99f94b2..f85d96d2c 100644
--- a/components/common/cpp/CMakeLists.txt
+++ b/components/common/cpp/CMakeLists.txt
@@ -1,6 +1,7 @@
 set(COMMONSRC
 	src/config.cpp
 	src/configuration.cpp
+	src/configurable.cpp
 	src/opencv_to_pcl.cpp
 )
 
diff --git a/components/common/cpp/include/ftl/configurable.hpp b/components/common/cpp/include/ftl/configurable.hpp
index 71c8a5564..d14585dc8 100644
--- a/components/common/cpp/include/ftl/configurable.hpp
+++ b/components/common/cpp/include/ftl/configurable.hpp
@@ -6,18 +6,35 @@
 #include <nlohmann/json.hpp>
 #include <string>
 #include <tuple>
+#include <map>
+#include <list>
+#include <functional>
 
 #define REQUIRED(...) required(__func__, __VA_ARGS__)
 
 namespace ftl {
 
+/**
+ * The Configurable class should be inherited by any entity that is to be
+ * configured using json objects. Additionally, any such object can then be
+ * reconfigured through changes to that underlying json object with event
+ * callbacks being triggered when specific changes occur.
+ * 
+ * Configurables may also optionally have a URI that enables static methods
+ * of getting and setting configuration options. These static methods are used
+ * by network RPCs to enable remote reconfiguration.
+ */
 class Configurable {
 	public:
 	Configurable() {}
 	explicit Configurable(nlohmann::json &config) : config_(config) {
-
+		if (config["uri"].is_string()) __changeURI(config["uri"].get<std::string>(), this);
 	}
 
+	/**
+	 * Force the JSON object to have specific properties with a specific type.
+	 * If not, emit errors and terminate the application.
+	 */
 	void required(const char *f, const std::vector<std::tuple<std::string, std::string, std::string>> &r) {
 		bool diderror = false;
 		for (auto i : r) {
@@ -33,10 +50,55 @@ class Configurable {
 
 	nlohmann::json &getConfig() { return config_; }
 
+	/**
+	 * Get a configuration property from the json object. Returns an optional
+	 * result which will be empty if the property does not exist or is not of
+	 * the requested type.
+	 */
+	template <typename T>
+	std::optional<T> get(const std::string &name) {
+		try {
+			return config_[name].get<T>();
+		} catch (...) {
+			return {};
+		}
+	}
+
+	/**
+	 * Change a configuration property and trigger any listening event handlers
+	 * for that property. Also triggers the global listeners.
+	 */
+	template <typename T>
+	void set(const std::string &name, T value) {
+		config_[name] = value;
+		_trigger(name);
+	}
+
+	/**
+	 * Add callback for whenever a specified property changes value.
+	 * @param prop Name of property to watch
+	 * @param callback A function object that will be called on change.
+	 */
+	void on(const std::string &prop, std::function<void(Configurable*, const std::string&)>);
+
 	protected:
 	nlohmann::json config_;
+
+	private:
+	std::map<std::string, std::list<std::function<void(Configurable*, const std::string&)>>> observers_; 
+
+	void _trigger(const std::string &name);
+
+	static void __changeURI(const std::string &uri, Configurable *cfg);
 };
 
+/*template <>
+void Configurable::set<const std::string&>(const std::string &name, const std::string &value) {
+	config_[name] = value;
+	if (name == "uri") __changeURI(value, this);
+	_trigger(name);
+}*/
+
 }
 
 #endif  // _FTL_CONFIGURABLE_HPP_
diff --git a/components/common/cpp/src/configurable.cpp b/components/common/cpp/src/configurable.cpp
new file mode 100644
index 000000000..cea4d74e6
--- /dev/null
+++ b/components/common/cpp/src/configurable.cpp
@@ -0,0 +1,24 @@
+#include <ftl/configurable.hpp>
+
+using ftl::Configurable;
+using std::string;
+using std::map;
+using std::list;
+using std::function;
+
+void Configurable::_trigger(const string &name) {
+	auto ix = observers_.find(name);
+	if (ix != observers_.end()) {
+		for (auto &f : (*ix).second) {
+			try {
+				f(this, name);
+			} catch(...) {
+				LOG(ERROR) << "Exception in event handler for '" << name << "'";
+			}
+		}
+	}
+}
+
+void Configurable::__changeURI(const string &uri, Configurable *cfg) {
+
+}
\ No newline at end of file
-- 
GitLab