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