From a5a045f091a19b9828e5406a9d1ec836ab81d5ab Mon Sep 17 00:00:00 2001
From: gereon <gereon.kremer@rwth-aachen.de>
Date: Fri, 23 Nov 2012 00:26:24 +0100
Subject: [PATCH] Changed Settings class to be singleton

Changed a lot in the class structure of Settings:

* Settings is now a Singleton. Obtain a new instance by Settings::instance().
* options_description objects are now static, thus a help message can be printed even if the constructor of Settings failes
* As Settings has static members, a .cpp file became necessary, hence cmake should probably be run...
* Help messages can be printed with new help and helpConfigfile routines
---
 src/mrmc-cpp.cpp         |   9 +-
 src/utility/settings.cpp | 179 +++++++++++++++++++++++++++++++++++++++
 src/utility/settings.h   | 167 ++++++++++++++----------------------
 3 files changed, 248 insertions(+), 107 deletions(-)
 create mode 100644 src/utility/settings.cpp

diff --git a/src/mrmc-cpp.cpp b/src/mrmc-cpp.cpp
index 2de514550..3c063a8e5 100644
--- a/src/mrmc-cpp.cpp
+++ b/src/mrmc-cpp.cpp
@@ -42,22 +42,23 @@ int main(const int argc, const char* argv[]) {
 	
 	try
 	{
-		s = new mrmc::settings::Settings(argc, argv, NULL);
+		s = mrmc::settings::Settings::instance(argc, argv, NULL);
 	}
 	catch (mrmc::exceptions::InvalidSettings)
 	{
-		std::cout << "Could not recover from settings error, terminating." << std::endl;
+		std::cout << "Could not recover from settings error, terminating." << std::endl << std::endl;
+		std::cout << mrmc::settings::help << std::endl;
 		return 1;
 	}
 	
 	if (s->isSet("help"))
 	{
-		std::cout << s->getHelpForCommandline() << std::endl;
+		std::cout << mrmc::settings::help << std::endl;
 		return 0;
 	}
 	if (s->isSet("help-config"))
 	{
-		std::cout << s->getHelpForConfigfile() << std::endl;
+		std::cout << mrmc::settings::helpConfigfile << std::endl;
 		return 0;
 	}
 
diff --git a/src/utility/settings.cpp b/src/utility/settings.cpp
new file mode 100644
index 000000000..daf08634b
--- /dev/null
+++ b/src/utility/settings.cpp
@@ -0,0 +1,179 @@
+/*
+ * settings.cpp
+ *
+ *  Created on: 22.11.2012
+ *      Author: Gereon Kremer
+ */
+
+#include "src/utility/settings.h"
+
+//#include <iostream>
+//#include <boost/program_options.hpp>
+//#include "src/exceptions/InvalidSettings.h"
+
+namespace mrmc {
+namespace settings {
+
+namespace bpo = boost::program_options;
+
+bpo::options_description mrmc::settings::Settings::configfile("Config Options");
+bpo::options_description mrmc::settings::Settings::generic("Generic Options");
+bpo::options_description mrmc::settings::Settings::commandline("Commandline Options");
+bpo::positional_options_description mrmc::settings::Settings::positional;
+
+bpo::options_description mrmc::settings::Settings::cli;
+bpo::options_description mrmc::settings::Settings::conf;
+
+mrmc::settings::Settings* mrmc::settings::Settings::inst = NULL;
+
+/*!
+ *	The constructor fills the option descriptions, parses the
+ *	command line and the config file and puts the option values to
+ *	our option mapping.
+ *
+ *	If a configfile is set in the commandline, we load this one.
+ *	Otherwise, if filename is not NULL, we load this one. Otherwise,
+ *	we load no config file.
+ *
+ *	@param argc should be argc passed to main function
+ *	@param argv should be argv passed to main function
+ *	@param filename	either NULL or name of config file
+ */
+Settings::Settings(const int argc, const char* argv[], const char* filename)
+{
+	try
+	{
+		/*
+		 *	load command line
+		 */
+		bpo::store(bpo::command_line_parser(argc, argv).options(this->cli).positional(this->positional).run(), this->vm);
+		/*
+		 *	load config file if specified
+		 */
+		if (this->vm.count("configfile"))
+		{
+			bpo::store(bpo::parse_config_file<char>(this->vm["configfile"].as<std::string>().c_str(), this->conf), this->vm, true);
+		}
+		else if (filename != NULL)
+		{
+			bpo::store(bpo::parse_config_file<char>(filename, this->conf), this->vm, true);
+		}
+		bpo::notify(this->vm);
+	}
+	/*
+	 *	catch errors...
+	 */
+	catch (bpo::reading_file e)
+	{
+		std::cout << "Could not read config file " << filename << std::endl;
+	}
+	catch (bpo::required_option e)
+	{
+		if (! (this->vm.count("help") || this->vm.count("help-config")))
+		{
+			std::cout << e.what() << std::endl;
+			throw mrmc::exceptions::InvalidSettings();
+		}
+	}
+	catch (bpo::error e)
+	{
+		std::cout << "Some error occurred: " << e.what() << std::endl;
+	}
+}
+
+/*!
+ *	Creates a new instance if necessary. When the instance is actually
+ *	created for the first time, the internal options_description objects are
+ *	initialized.
+ *	
+ *	If this function was already called and another instance is   
+ *	already present, the existing instance will be returned. In this
+ *	case, better use the routine mrmc::settings::instance().
+ *
+ *	The constructor fills the option descriptions, parses the
+ *	command line and the config file and puts the option values to
+ *	our option mapping.
+ *
+ *	If a configfile is set in the commandline, we load this one.
+ *	Otherwise, if filename is not NULL, we load this one. Otherwise,
+ *	we load no config file.
+ *
+ *	@param argc should be argc passed to main function
+ *	@param argv should be argv passed to main function
+ *	@param filename	either NULL or name of config file
+ *	@return instance of Settings
+ */
+Settings* Settings::instance(const int argc, const char* argv[], const char* filename)
+{
+	if (Settings::inst == NULL) {
+		/*
+		 *	fill option descriptions
+		 */
+		Settings::commandline.add_options()
+			("help", "produce help message")
+			("help-config", "produce help message about config file")
+			("configfile", bpo::value<std::string>(), "name of config file")
+		;
+		Settings::generic.add_options()
+			("trafile", bpo::value<std::string>()->required(), "name of the .tra file")
+			("labfile", bpo::value<std::string>()->required(), "name of the .lab file")
+		;
+		Settings::configfile.add_options()
+		;
+
+		/*
+		 *	construct option descriptions for commandline and config file
+		 */
+		Settings::cli.add(Settings::commandline).add(generic);
+		Settings::conf.add(Settings::configfile).add(generic);
+	
+		/*
+		 *	Take care of positional arguments
+		 */
+		Settings::positional.add("trafile", 1);
+		Settings::positional.add("labfile", 1);
+		
+		/*
+		 *	Actually create new instance
+		 */
+		Settings::inst = new Settings(argc, argv, filename);	        
+	}
+	return Settings::inst;
+}
+
+/*!
+ *	Print a short general usage information consisting of the positional
+ *	options and the list of available command line options.
+ *
+ *	Use it like this:
+ *	@code std::cout << mrmc::settings::help; @endcode
+ */
+std::ostream& help(std::ostream& os)
+{
+	os << "Usage: <binary> [options] <transition file> <label file>" << std::endl;
+	os << Settings::cli << std::endl;;
+	return os;
+}
+
+/*!
+ *	Print a list of available options for the config file.
+ *
+ *	Use it like this:
+ *	@code std::cout << mrmc::settings::helpConfigfile; @endcode
+ */
+std::ostream& helpConfigfile(std::ostream& os)
+{
+	os << Settings::conf << std::endl;;
+	return os;
+}
+
+/*!
+ *	@return current instance.
+ */
+Settings* instance()
+{
+	return Settings::inst;
+}
+    
+} // namespace settings
+} // namespace mrmc
\ No newline at end of file
diff --git a/src/utility/settings.h b/src/utility/settings.h
index 991862f0b..3331baa83 100644
--- a/src/utility/settings.h
+++ b/src/utility/settings.h
@@ -13,155 +13,116 @@
 #include "src/exceptions/InvalidSettings.h"
 
 namespace mrmc {
-namespace settings {
 
+/*!
+ *	The settings namespace contains the Settings class and all associated
+ *	methods.
+ */
+namespace settings {
+	
 	namespace bpo = boost::program_options;
 	/*!
 	 *	@brief	Simple wrapper around boost::program_options to handle configuration options.
 	 *
 	 *	This class uses boost::program_options to read options from the
 	 *	commandline and additionally load options from a file.
+	 *
+	 *	It is meant to be used as a singleton. Call 
+	 *	@code mrmc::settings::Settings::instance(argc, argv, filename) @endcode
+	 *	to initialize it and obtain an instance for the first time.
+	 *	Afterwards, it is possible to use
+	 *	@code mrmc::settings::instance() @endcode
 	 */
 	class Settings
 	{
 		public:
 		
-		/*!
-		 *	@brief Constructor of Settings file
-		 *
-		 *	The constructor fills the option descriptions, parses the
-		 *	command line and the config file and puts the option values to
-		 *	our option mapping.
-		 *
-		 *	If a configfile is set in the commandline, we load this one.
-		 *	Otherwise, if filename is not NULL, we load this one. Otherwise,
-		 *	we load no config file.
-		 *
-		 *	@param argc should be argc passed to main function
-		 *	@param argv should be argv passed to main function
-		 *	@param filename	either NULL or name of config file
-		 */
-		Settings(const int argc, const char* argv[], const char* filename)
-			: configfile("Config Options"), generic("Generic Options"), commandline("Commandline Options")
-		{
-			/*
-			 *	fill option descriptions
-			 */
-			this->commandline.add_options()
-				("help", "produce help message")
-				("help-config", "produce help message about config file")
-				("configfile", bpo::value<std::string>(), "name of config file")
-			;
-			this->generic.add_options()
-				("trafile", bpo::value<std::string>()->required(), "name of the .tra file")
-				("labfile", bpo::value<std::string>()->required(), "name of the .lab file")
-			;
-			this->configfile.add_options()
-			;
-			
-			this->positional.add("trafile", 1);
-			this->positional.add("labfile", 1);
-			
-			/*
-			 *	construct option descriptions for commandline and config file
-			 */
-			this->cli.add(this->commandline).add(generic);
-			this->conf.add(this->configfile).add(generic);
-			
-			try
-			{
-				/*
-				 *	load command line
-				 */
-				bpo::store(bpo::command_line_parser(argc, argv).options(this->cli).positional(this->positional).run(), this->vm);
-				/*
-				 *	load config file if specified
-				 */
-				if (this->vm.count("configfile"))
-				{
-					bpo::store(bpo::parse_config_file<char>(this->vm["configfile"].as<std::string>().c_str(), this->conf), this->vm, true);
-				}
-				else if (filename != NULL)
-				{
-					bpo::store(bpo::parse_config_file<char>(filename, this->conf), this->vm, true);
-				}
-				bpo::notify(this->vm);
-			}
-			/*
-			 *	catch errors...
-			 */
-			catch (bpo::reading_file e)
-			{
-				std::cout << "Could not read config file " << filename << std::endl;
-			}
-			catch (bpo::required_option e)
-			{
-				if (! (this->vm.count("help") || this->vm.count("help-config")))
-				{
-					std::cout << e.what() << std::endl;
-					throw mrmc::exceptions::InvalidSettings();
-				}
-			}
-			catch (bpo::error e)
-			{
-				std::cout << "Some error occurred: " << e.what() << std::endl;
-			}
-		}
-		
 		/*!
 		 *	@brief Get option descriptions for commandline options
 		 */
-		bpo::options_description& getHelpForCommandline()
-		{
+		bpo::options_description& getHelpForCommandline() const {
 			return this->cli;
 		}
 
 		/*!
 		 *	@brief Get option descriptions for config file options
 		 */
-		bpo::options_description& getHelpForConfigfile()
-		{
+		bpo::options_description& getHelpForConfigfile() const {
 			return this->conf;
 		}
 		
 		/*!
-		 *	@brief Get value of string argument
+		 *	@brief Get value of string option
 		 */
-		const std::string& getString(const std::string &name) const
-		{
+		const std::string& getString(const std::string &name) const {
 			return this->vm[name].as<std::string>();
 		}
 		
-		const bool isSet(const std::string &name) const
-		{
+		/*!
+		 *	@brief Check if an option is set
+		 */
+		const bool isSet(const std::string &name) const {
 			return this->vm.count(name) > 0;
 		}
+	
+		friend std::ostream& help(std::ostream& os);
+		friend std::ostream& helpConfigfile(std::ostream& os);
+		friend Settings* instance();
+
+		/*!
+		 *	@brief Creates a new instance.
+		 */
+		static Settings* instance(const int argc, const char* argv[], const char* filename);
 
 		private:
 			/*!
-			 *	@brief option descriptions
+			 *	@brief	Constructor.
+			 */
+			Settings(const int argc, const char* argv[], const char* filename);
+		
+			/*!
+			 *	@brief Option descriptions.
 			 */
-			bpo::options_description configfile;
-			bpo::options_description generic;
-			bpo::options_description commandline;
-			bpo::positional_options_description positional;
+			static bpo::options_description configfile;
+			static bpo::options_description generic;
+			static bpo::options_description commandline;
+			static bpo::positional_options_description positional;
 			
 			/*!
-			 *	@brief collecing option descriptions
+			 *	@brief Collecting option descriptions.
 			 *
 			 *	The options for command line and config file are collected
-			 *	here
+			 *	here.
 			 */
-			bpo::options_description cli;
-			bpo::options_description conf;
+			static bpo::options_description cli;
+			static bpo::options_description conf;
 			
 			/*!
-			 *	@brief	option mapping
+			 *	@brief	option mapping.
 			 */
 			bpo::variables_map vm;
-		
+			
+			/*!
+			 *	@brief	actual instance of this class.
+			 */
+			static Settings* inst;
 	};
 
+	/*!
+	 *	@brief	Print usage help.
+	 */
+	std::ostream& help(std::ostream& os);
+	
+	/*!
+	 *	@brief	Print help for config file options.
+	 */
+	std::ostream& helpConfigfile(std::ostream& os);
+	
+	/*!
+	 *	@brief	Return current instance.
+	 */
+	Settings* instance();
+
 } // namespace parser
 } // namespace mrmc