Browse Source

completed work on intermediate callbacks and restructured settings class

main
gereon 13 years ago
parent
commit
cd4463116e
  1. 2
      src/mrmc.cpp
  2. 316
      src/utility/settings.cpp
  3. 84
      src/utility/settings.h

2
src/mrmc.cpp

@ -74,7 +74,7 @@ int main(const int argc, const char* argv[]) {
LOG4CPLUS_INFO(logger, "MRMC command invoked " << commandStream.str()); LOG4CPLUS_INFO(logger, "MRMC command invoked " << commandStream.str());
try { try {
s = mrmc::settings::Settings::instance(argc, argv, nullptr); s = mrmc::settings::newInstance(argc, argv, nullptr);
} catch (mrmc::exceptions::InvalidSettings&) { } catch (mrmc::exceptions::InvalidSettings&) {
LOG4CPLUS_FATAL(logger, "Could not recover from settings error, terminating."); LOG4CPLUS_FATAL(logger, "Could not recover from settings error, terminating.");
std::cout << std::endl << mrmc::settings::help << std::endl; std::cout << std::endl << mrmc::settings::help << std::endl;

316
src/utility/settings.cpp

@ -7,24 +7,17 @@
#include "src/utility/settings.h" #include "src/utility/settings.h"
#include <iostream>
//#include <boost/program_options.hpp>
//#include "src/exceptions/InvalidSettings.h"
namespace mrmc { namespace mrmc {
namespace settings { namespace settings {
namespace bpo = boost::program_options; namespace bpo = boost::program_options;
bpo::options_description mrmc::settings::Settings::configfile("Config Options"); /*
bpo::options_description mrmc::settings::Settings::generic("Generic Options"); * static initializers
bpo::options_description mrmc::settings::Settings::commandline("Commandline Options"); */
bpo::positional_options_description mrmc::settings::Settings::positional; bpo::options_description* mrmc::settings::Settings::cli = nullptr;
bpo::options_description* mrmc::settings::Settings::conf = nullptr;
bpo::options_description mrmc::settings::Settings::cli; mrmc::settings::Settings* mrmc::settings::Settings::inst = nullptr;
bpo::options_description mrmc::settings::Settings::conf;
mrmc::settings::Settings* mrmc::settings::Settings::inst = NULL;
/*! /*!
* The constructor fills the option descriptions, parses the * The constructor fills the option descriptions, parses the
@ -37,163 +30,220 @@ mrmc::settings::Settings* mrmc::settings::Settings::inst = NULL;
* *
* @param argc should be argc passed to main function * @param argc should be argc passed to main function
* @param argv should be argv passed to main function * @param argv should be argv passed to main function
* @param filename either NULL or name of config file * @param filename either nullptr or name of config file
*/ */
Settings::Settings(const int argc, const char* argv[], const char* filename) Settings::Settings(const int argc, const char* argv[], const char* filename)
: configfile("Config Options"), generic("Generic Options"), commandline("Commandline Options")
{ {
try try
{ {
/* //! Initially fill description objects and call register callbacks
* load command line this->initDescriptions();
*/ //! Take care of positional arguments
bpo::store(bpo::command_line_parser(argc, argv).options(this->cli).positional(this->positional).run(), this->vm); Settings::positional.add("trafile", 1);
/* Settings::positional.add("labfile", 1);
* load config file if specified //! Create and fill collecting options descriptions
*/ Settings::cli = new bpo::options_description();
if (this->vm.count("configfile")) Settings::cli->add(Settings::commandline).add(generic);
{ Settings::conf = new bpo::options_description();
bpo::store(bpo::parse_config_file<char>(this->vm["configfile"].as<std::string>().c_str(), this->conf), this->vm, true); Settings::conf->add(Settings::configfile).add(generic);
} //! Perform first parse run and call intermediate callbacks
else if (filename != NULL) this->firstRun(argc, argv, filename);
{
bpo::store(bpo::parse_config_file<char>(filename, this->conf), this->vm, true);
}
/*
* If a required option is missing, this will throw a bpo::required_option.
* This exception is catched below.
*/
bpo::notify(this->vm);
/* //! Rebuild collecting options descriptions
* Call custom option checker. delete Settings::cli;
*/ Settings::cli = new bpo::options_description();
Callbacks* cb = mrmc::settings::Callbacks::getInstance(); Settings::cli->add(Settings::commandline).add(generic);
while (cb->checkerList.size() > 0) delete Settings::conf;
Settings::conf = new bpo::options_description();
Settings::conf->add(Settings::configfile).add(generic);
//! Stop if help is set
if ((this->vm.count("help") > 0) || (this->vm.count("help-config") > 0))
{ {
CheckerCallback fptr = cb->checkerList.front(); return;
cb->checkerList.pop_front();
if (! (*fptr)(this->vm))
{
std::cerr << "Custom option checker failed." << std::endl;
throw mrmc::exceptions::InvalidSettings();
}
} }
//! Perform second run and call checker callbacks
this->secondRun(argc, argv, filename);
//! Finalize parsed options, check for specified requirements
bpo::notify(this->vm);
} }
/*
* catch errors...
*/
catch (bpo::reading_file e) catch (bpo::reading_file e)
{ {
std::cerr << "Could not read config file " << filename << std::endl; std::cerr << "Could not read config file " << filename << std::endl;
} }
catch (bpo::required_option e) catch (bpo::required_option e)
{ {
if (! (this->vm.count("help") || this->vm.count("help-config"))) std::cerr << "required option: " << e.what() << std::endl;
{ throw mrmc::exceptions::InvalidSettings();
std::cerr << e.what() << std::endl; }
throw mrmc::exceptions::InvalidSettings(); catch (bpo::validation_error e)
} {
std::cerr << "Validation failed: " << e.what() << std::endl;
throw mrmc::exceptions::InvalidSettings();
}
catch (bpo::invalid_command_line_syntax e)
{
std::cerr << "Invalid command line syntax: " << e.what() << std::endl;
throw mrmc::exceptions::InvalidSettings();
} }
catch (bpo::error e) catch (bpo::error e)
{ {
std::cerr << "Some error occurred: " << e.what() << std::endl; std::cerr << e.what() << std::endl;
throw mrmc::exceptions::InvalidSettings();
} }
} }
/*! /*!
* Creates a new instance if necessary. When the instance is actually * Initially fill options_description objects.
* created for the first time, the internal options_description objects are * First puts some generic options, then calls all register Callbacks.
* initialized. */
* void Settings::initDescriptions()
* If this function was already called and another instance is {
* already present, the existing instance will be returned. In this this->commandline.add_options()
* case, better use the routine mrmc::settings::instance(). ("help,h", "produce help message")
* ("verbose,v", "be verbose")
* The constructor fills the option descriptions, parses the ("help-config", "produce help message about config file")
* command line and the config file and puts the option values to ("configfile,c", bpo::value<std::string>(), "name of config file")
* our option mapping. It also calls all register callbacks that were ("test-prctl", bpo::value<std::string>(), "name of prctl file")
* registered via Callbacks and Register classes. ;
* this->generic.add_options()
* If a configfile is set in the commandline, we load this one. ("trafile", bpo::value<std::string>()->required(), "name of the .tra file")
* Otherwise, if filename is not NULL, we load this one. Otherwise, ("labfile", bpo::value<std::string>()->required(), "name of the .lab file")
* we load no config file. ;
this->configfile.add_options()
;
/*
* Get Callbacks object, then iterate over and call all register callbacks.
*/
Callbacks* cb = mrmc::settings::Callbacks::getInstance();
while (cb->registerList.size() > 0)
{
CallbackType type = cb->registerList.front().first;
RegisterCallback fptr = cb->registerList.front().second;
cb->registerList.pop_front();
switch (type)
{
case CB_CONFIG:
(*fptr)(this->configfile);
break;
case CB_CLI:
(*fptr)(this->commandline);
break;
case CB_GENERIC:
(*fptr)(this->generic);
break;
}
}
}
/*!
* Perform a sloppy parsing run: parse command line and config file (if
* given), but allow for unregistered options, do not check requirements
* from options_description objects, do not check positional arguments.
* *
* @param argc should be argc passed to main function * Call all intermediate callbacks afterwards.
* @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) void Settings::firstRun(const int argc, const char* argv[], const char* filename)
{ {
if (Settings::inst == NULL) { //! parse command line
/* bpo::store(bpo::command_line_parser(argc, argv).options(*(Settings::cli)).allow_unregistered().run(), this->vm);
* fill option descriptions /*
*/ * load config file if specified
Settings::commandline.add_options() */
("help,h", "produce help message") if (this->vm.count("configfile"))
("verbose,v", "be verbose") {
("help-config", "produce help message about config file") bpo::store(bpo::parse_config_file<char>(this->vm["configfile"].as<std::string>().c_str(), *(Settings::conf)), this->vm, true);
("configfile,c", bpo::value<std::string>(), "name of config file") }
("test-prctl", bpo::value<std::string>(), "name of prctl file") else if (filename != NULL)
; {
Settings::generic.add_options() bpo::store(bpo::parse_config_file<char>(filename, *(Settings::conf)), this->vm, true);
("trafile", bpo::value<std::string>()->required(), "name of the .tra file") }
("labfile", bpo::value<std::string>()->required(), "name of the .lab file") /*
; * Call intermediate callbacks.
Settings::configfile.add_options() */
; Callbacks* cb = mrmc::settings::Callbacks::getInstance();
while (cb->intermediateList.size() > 0)
{
CallbackType type = cb->intermediateList.front().first;
IntermediateCallback fptr = cb->intermediateList.front().second;
cb->intermediateList.pop_front();
/* try
* Call all custom register callbacks.
*/
Callbacks* cb = mrmc::settings::Callbacks::getInstance();
while (cb->registerList.size() > 0)
{ {
CallbackType type = cb->registerList.front().first;
RegisterCallback fptr = cb->registerList.front().second;
cb->registerList.pop_front();
/*
* Allow modules to specify the type of an option.
*/
switch (type) switch (type)
{ {
case CB_CONFIG: case CB_CONFIG:
(*fptr)(Settings::configfile); (*fptr)(&this->configfile, this->vm);
break; break;
case CB_CLI: case CB_CLI:
(*fptr)(Settings::commandline); (*fptr)(&this->commandline, this->vm);
break; break;
case CB_GENERIC: case CB_GENERIC:
(*fptr)(Settings::generic); (*fptr)(&this->generic, this->vm);
break;
default:
// hm. is this an error? can this actually happen with an enum?
break; break;
} }
} }
catch (boost::bad_any_cast e)
{
std::cerr << "An intermediate callback failed." << std::endl;
std::cerr << e.what() << std::endl;
}
}
}
/* /*!
* construct option descriptions for commandline and config file * Perform the second parser run: parse command line and config file (if
*/ * given) and check for unregistered options, requirements from
Settings::cli.add(Settings::commandline).add(generic); * options_description objects and positional arguments.
Settings::conf.add(Settings::configfile).add(generic); *
* Call all checker callbacks afterwards.
*/
void Settings::secondRun(const int argc, const char* argv[], const char* filename)
{
//! Parse command line
bpo::store(bpo::command_line_parser(argc, argv).options(*(Settings::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(), *(Settings::conf)), this->vm, true);
}
else if (filename != NULL)
{
bpo::store(bpo::parse_config_file<char>(filename, *(Settings::conf)), this->vm, true);
}
/* /*
* Take care of positional arguments * Call checker callbacks.
*/ */
Settings::positional.add("trafile", 1); Callbacks* cb = mrmc::settings::Callbacks::getInstance();
Settings::positional.add("labfile", 1); while (cb->checkerList.size() > 0)
{
CheckerCallback fptr = cb->checkerList.front();
cb->checkerList.pop_front();
/* if (! (*fptr)(this->vm))
* Actually create new instance {
*/ std::cerr << "Custom option checker failed." << std::endl;
Settings::inst = new Settings(argc, argv, filename); throw mrmc::exceptions::InvalidSettings();
}
} }
return Settings::inst;
} }
/*! /*!
* Print a short general usage information consisting of the positional * Print a short general usage information consisting of the positional
* options and the list of available command line options. * options and the list of available command line options.
@ -204,7 +254,7 @@ Settings* Settings::instance(const int argc, const char* argv[], const char* fil
std::ostream& help(std::ostream& os) std::ostream& help(std::ostream& os)
{ {
os << "Usage: <binary> [options] <transition file> <label file>" << std::endl; os << "Usage: <binary> [options] <transition file> <label file>" << std::endl;
os << Settings::cli << std::endl;; os << *(mrmc::settings::Settings::cli) << std::endl;;
return os; return os;
} }
@ -216,17 +266,9 @@ std::ostream& help(std::ostream& os)
*/ */
std::ostream& helpConfigfile(std::ostream& os) std::ostream& helpConfigfile(std::ostream& os)
{ {
os << Settings::conf << std::endl;; os << *(mrmc::settings::Settings::conf) << std::endl;;
return os; return os;
} }
/*!
* @return current instance.
*/
Settings* instance()
{
return Settings::inst;
}
} // namespace settings } // namespace settings
} // namespace mrmc } // namespace mrmc

84
src/utility/settings.h

@ -45,6 +45,8 @@ namespace settings {
*/ */
template <typename T> template <typename T>
const T& get(const std::string &name) const { const T& get(const std::string &name) const {
std::cerr << "get(" << name << ")" << std::endl;
if (this->vm.count(name) == 0) throw mrmc::exceptions::InvalidSettings();
return this->vm[name].as<T>(); return this->vm[name].as<T>();
} }
@ -65,34 +67,54 @@ namespace settings {
friend std::ostream& help(std::ostream& os); friend std::ostream& help(std::ostream& os);
friend std::ostream& helpConfigfile(std::ostream& os); friend std::ostream& helpConfigfile(std::ostream& os);
friend Settings* instance(); friend Settings* instance();
friend Settings* newInstance(const int argc, const char* argv[], const char* filename);
/*!
* @brief Creates a new instance.
*/
static Settings* instance(const int argc, const char* argv[], const char* filename);
private: private:
/*! /*!
* @brief Constructor. * @brief Constructor.
*/ */
Settings(const int argc, const char* argv[], const char* filename); Settings(const int argc, const char* argv[], const char* filename);
/*! /*!
* @brief Option descriptions. * @brief Initialize options_description object.
*/ */
static bpo::options_description configfile; void initDescriptions();
static bpo::options_description generic;
static bpo::options_description commandline;
static bpo::positional_options_description positional;
/*! /*!
* @brief Collecting option descriptions. * @brief Perform first parser run
* */
* The options for command line and config file are collected void firstRun(const int argc, const char* argv[], const char* filename);
* here. /*!
* @brief Perform second parser run.
*/ */
static bpo::options_description cli; void secondRun(const int argc, const char* argv[], const char* filename);
static bpo::options_description conf; /*!
* @brief Option descriptions for config file.
*/
bpo::options_description configfile;
/*!
* @brief Option descriptions for config file and command line.
*/
bpo::options_description generic;
/*!
* @brief Option descriptions for command line.
*/
bpo::options_description commandline;
/*!
* @brief Option description for positional arguments on command line.
*/
bpo::positional_options_description positional;
/*!
* @brief Collecting option descriptions for command line.
*/
static bpo::options_description* cli;
/*!
* @brief Collecting option descriptions for config file.
*/
static bpo::options_description* conf;
/*! /*!
* @brief option mapping. * @brief option mapping.
@ -117,8 +139,30 @@ namespace settings {
/*! /*!
* @brief Return current instance. * @brief Return current instance.
*
* @return The current instance of Settings created by newInstance().
*/ */
Settings* instance(); 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;
}
/*! /*!
@ -130,7 +174,7 @@ namespace settings {
* @brief Function type for functions changing the parser state * @brief Function type for functions changing the parser state
* between the first and second run. * between the first and second run.
*/ */
typedef void(*IntermediateCallback)(bpo::options_description&, bpo::variables_map&); typedef void(*IntermediateCallback)(bpo::options_description*, bpo::variables_map&);
/*! /*!
* @brief Function type for function checking constraints on settings. * @brief Function type for function checking constraints on settings.
@ -194,7 +238,7 @@ namespace settings {
* @brief Returns current instance to create singleton. * @brief Returns current instance to create singleton.
* @return current instance * @return current instance
*/ */
static Callbacks* getInstance() inline static Callbacks* getInstance()
{ {
static Callbacks instance; static Callbacks instance;
return &instance; return &instance;

|||||||
100:0
Loading…
Cancel
Save