You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							218 lines
						
					
					
						
							6.1 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							218 lines
						
					
					
						
							6.1 KiB
						
					
					
				| /* | |
|  * settings.h | |
|  * | |
|  *  Created on: 22.11.2012 | |
|  *      Author: Gereon Kremer | |
|  */ | |
| 
 | |
| #ifndef SETTINGS_H_ | |
| #define SETTINGS_H_ | |
|  | |
| #include <iostream> | |
| #include <sstream> | |
| #include <list> | |
| #include <utility> | |
| #include <memory> | |
| #include <boost/program_options.hpp> | |
| #include "src/exceptions/InvalidSettings.h" | |
|  | |
| namespace mrmc { | |
| 
 | |
| /*! | |
|  *	@brief Contains Settings class and associated methods. | |
|  * | |
|  *	The settings namespace contains the Settings class some friend methods like instance(). | |
|  */ | |
| namespace settings { | |
| 	 | |
| 	namespace bpo = boost::program_options; | |
| 	 | |
| 	/* | |
| 	 *	Sorry for very long comment at this point (for the class), but all | |
| 	 *	methods are private, hence there is no other place to explain the | |
| 	 *	inner workings of this class that are necessary to understand the | |
| 	 *	callback concept... | |
| 	 */ | |
| 	/*! | |
| 	 *	@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::newInstance(argc, argv, filename) @endcode | |
| 	 *	to initialize it and obtain an instance for the first time. | |
| 	 *	Afterwards, use | |
| 	 *	@code mrmc::settings::instance() @endcode | |
| 	 * | |
| 	 *	This class can be customized by other parts of the software using | |
| 	 *	callbacks. There are three types of callbacks: register, | |
| 	 *	intermediate and checker. | |
| 	 * | |
| 	 *	The (private) constructor will start with filling the internal | |
| 	 *	options_description object. There are a few generic options like | |
| 	 *	--help or --verbose. Then if calls all register callbacks that may | |
| 	 *	add more options. | |
| 	 * | |
| 	 *	Then, it will start with a sloppy parsing run, allowing unregistered  | |
| 	 *	options and ignoring further constraints from the options_description  | |
| 	 *	objects. | |
| 	 * | |
| 	 *	After that, it will call all intermediate callbacks. They can | |
| 	 *	inspect the options from the first run and add more options, e.g. | |
| 	 *	enable more options for a specific component that has been enabled. | |
| 	 * | |
| 	 *	Using the new options_description objects, the constructor performs | |
| 	 *	a second run. This time, it will not allow unregistered options and | |
| 	 *	will check for required and positional arguments. | |
| 	 * | |
| 	 *	Finally, all checker callbacks will be called. They can check the | |
| 	 *	final options for more complex requirements. If any of those checker | |
| 	 *	callbacks returns false, a InvalidSettings exception will be thrown. | |
| 	 */ | |
| 	class Settings | |
| 	{ | |
| 		public: | |
| 		 | |
| 			/*! | |
| 			 *	@brief	Get value of a generic option. | |
| 			 */ | |
| 			template <typename T> | |
| 			const T& get(const std::string &name) const { | |
| 				if (this->vm.count(name) == 0) throw mrmc::exceptions::InvalidSettings(); | |
| 				return this->vm[name].as<T>(); | |
| 			} | |
| 		 | |
| 			/*! | |
| 			 *	@brief	Get value of string option | |
| 			 */ | |
| 			const std::string& getString(const std::string &name) const { | |
| 				return this->get<std::string>(name); | |
| 			} | |
| 		 | |
| 			/*! | |
| 			 *	@brief	Check if an option is set | |
| 			 */ | |
| 			const bool isSet(const std::string &name) const { | |
| 				return this->vm.count(name) > 0; | |
| 			} | |
| 			 | |
| 			/*! | |
| 			 *	@brief	Register a new module | |
| 			 * | |
| 			 *	This function implicitly defines the following interface for any SettingsModule: | |
| 			 *	@code | |
| 			 *	static std::string getModuleName(); | |
| 			 *	static std::pair< std::string, std::string > getOptionTrigger(); | |
| 			 *	static void putOptions(boost::program_options::options_description*); | |
| 			 *	@endcode | |
| 			 */ | |
| 			template <typename T> | |
| 			static void registerModule() | |
| 			{ | |
| 				std::pair< std::string, std::string > trigger = T::getOptionTrigger(); | |
| 				std::stringstream str; | |
| 				str << T::getModuleName() << " (" << trigger.first << " = " << trigger.second << ")"; | |
| 				bpo::options_description* desc = new bpo::options_description(str.str()); | |
| 				T::putOptions(desc); | |
| 				Settings::modules[ trigger ] = desc; | |
| 			} | |
| 	 | |
| 			friend std::ostream& help(std::ostream& os); | |
| 			friend std::ostream& helpConfigfile(std::ostream& os); | |
| 			friend Settings* instance(); | |
| 			friend Settings* newInstance(const int argc, const char* argv[], const char* filename); | |
| 
 | |
| 		private: | |
| 			/*! | |
| 			 *	@brief	Constructor. | |
| 			 */ | |
| 			Settings(const int argc, const char* argv[], const char* filename); | |
| 			 | |
| 			/*! | |
| 			 *	@brief	Initialize options_description object. | |
| 			 */ | |
| 			void initDescriptions(); | |
| 			 | |
| 			/*! | |
| 			 *	@brief	Perform first parser run | |
| 			 */ | |
| 			void firstRun(const int argc, const char* argv[], const char* filename); | |
| 			 | |
| 			/*! | |
| 			 *	@brief	Perform second parser run. | |
| 			 */ | |
| 			void secondRun(const int argc, const char* argv[], const char* filename); | |
| 			 | |
| 			/*! | |
| 			 *	@brief	Option description for positional arguments on command line. | |
| 			 */ | |
| 			bpo::positional_options_description positional; | |
| 			 | |
| 			/*! | |
| 			 *	@brief	Collecting option descriptions. | |
| 			 */ | |
| 			static std::unique_ptr<bpo::options_description> desc; | |
| 
 | |
| 			/*! | |
| 			 *	@brief	Contains option descriptions for all modules. | |
| 			 */ | |
| 			static std::map< std::pair< std::string, std::string >, bpo::options_description* > modules; | |
| 			 | |
| 			/*! | |
| 			 *	@brief	option mapping. | |
| 			 */ | |
| 			bpo::variables_map vm; | |
| 			 | |
| 			/*! | |
| 			 *	@brief name of binary | |
| 			 */ | |
| 			static std::string binaryName; | |
| 			 | |
| 			/*! | |
| 			 *	@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. | |
| 	 * | |
| 	 *	@return The current instance of Settings created by newInstance(). | |
| 	 */ | |
| 	inline Settings* instance() | |
| 	{ | |
| 		return Settings::inst; | |
| 	} | |
| 	 | |
| 	/*! | |
| 	 *	@brief	Create new instance. | |
| 	 * | |
| 	 *	Creates a new Settings instance and passes the arguments to the constructor of Settings. | |
| 	 * | |
| 	 *	@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 The new instance of Settings. | |
| 	 */ | |
| 	inline Settings* newInstance(const int argc, const char* argv[], const char* filename) | |
| 	{ | |
| 		if (Settings::inst != nullptr) delete Settings::inst; | |
| 		Settings::inst = new Settings(argc, argv, filename); | |
| 		return Settings::inst; | |
| 	} | |
| 		 | |
| } // namespace settings | |
| } // namespace mrmc | |
|  | |
| #endif // SETTINGS_H_
 |