|
|
@ -7,24 +7,17 @@ |
|
|
|
|
|
|
|
#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; |
|
|
|
/*
|
|
|
|
* static initializers |
|
|
|
*/ |
|
|
|
bpo::options_description* mrmc::settings::Settings::cli = nullptr; |
|
|
|
bpo::options_description* mrmc::settings::Settings::conf = nullptr; |
|
|
|
mrmc::settings::Settings* mrmc::settings::Settings::inst = nullptr; |
|
|
|
|
|
|
|
/*!
|
|
|
|
* The constructor fills the option descriptions, parses the |
|
|
@ -37,115 +30,98 @@ mrmc::settings::Settings* mrmc::settings::Settings::inst = NULL; |
|
|
|
* |
|
|
|
* @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 |
|
|
|
* @param filename either nullptr or name of config file |
|
|
|
*/ |
|
|
|
Settings::Settings(const int argc, const char* argv[], const char* filename) |
|
|
|
: configfile("Config Options"), generic("Generic Options"), commandline("Commandline Options") |
|
|
|
{ |
|
|
|
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); |
|
|
|
} |
|
|
|
/*
|
|
|
|
* If a required option is missing, this will throw a bpo::required_option. |
|
|
|
* This exception is catched below. |
|
|
|
*/ |
|
|
|
bpo::notify(this->vm); |
|
|
|
//! Initially fill description objects and call register callbacks
|
|
|
|
this->initDescriptions(); |
|
|
|
|
|
|
|
/*
|
|
|
|
* Call custom option checker. |
|
|
|
*/ |
|
|
|
Callbacks* cb = mrmc::settings::Callbacks::getInstance(); |
|
|
|
while (cb->checkerList.size() > 0) |
|
|
|
{ |
|
|
|
CheckerCallback fptr = cb->checkerList.front(); |
|
|
|
cb->checkerList.pop_front(); |
|
|
|
//! Take care of positional arguments
|
|
|
|
Settings::positional.add("trafile", 1); |
|
|
|
Settings::positional.add("labfile", 1); |
|
|
|
|
|
|
|
if (! (*fptr)(this->vm)) |
|
|
|
//! Create and fill collecting options descriptions
|
|
|
|
Settings::cli = new bpo::options_description(); |
|
|
|
Settings::cli->add(Settings::commandline).add(generic); |
|
|
|
Settings::conf = new bpo::options_description(); |
|
|
|
Settings::conf->add(Settings::configfile).add(generic); |
|
|
|
|
|
|
|
//! Perform first parse run and call intermediate callbacks
|
|
|
|
this->firstRun(argc, argv, filename); |
|
|
|
|
|
|
|
//! Rebuild collecting options descriptions
|
|
|
|
delete Settings::cli; |
|
|
|
Settings::cli = new bpo::options_description(); |
|
|
|
Settings::cli->add(Settings::commandline).add(generic); |
|
|
|
|
|
|
|
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)) |
|
|
|
{ |
|
|
|
std::cerr << "Custom option checker failed." << std::endl; |
|
|
|
throw mrmc::exceptions::InvalidSettings(); |
|
|
|
} |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
//! 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) |
|
|
|
{ |
|
|
|
std::cerr << "Could not read config file " << filename << std::endl; |
|
|
|
} |
|
|
|
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(); |
|
|
|
} |
|
|
|
catch (bpo::validation_error e) |
|
|
|
{ |
|
|
|
std::cerr << e.what() << std::endl; |
|
|
|
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) |
|
|
|
{ |
|
|
|
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 |
|
|
|
* 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. It also calls all register callbacks that were |
|
|
|
* registered via Callbacks and Register classes. |
|
|
|
* |
|
|
|
* 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 |
|
|
|
* Initially fill options_description objects. |
|
|
|
* First puts some generic options, then calls all register Callbacks. |
|
|
|
*/ |
|
|
|
Settings* Settings::instance(const int argc, const char* argv[], const char* filename) |
|
|
|
void Settings::initDescriptions() |
|
|
|
{ |
|
|
|
if (Settings::inst == NULL) { |
|
|
|
/*
|
|
|
|
* fill option descriptions |
|
|
|
*/ |
|
|
|
Settings::commandline.add_options() |
|
|
|
this->commandline.add_options() |
|
|
|
("help,h", "produce help message") |
|
|
|
("verbose,v", "be verbose") |
|
|
|
("help-config", "produce help message about config file") |
|
|
|
("configfile,c", bpo::value<std::string>(), "name of config file") |
|
|
|
("test-prctl", bpo::value<std::string>(), "name of prctl file") |
|
|
|
; |
|
|
|
Settings::generic.add_options() |
|
|
|
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") |
|
|
|
; |
|
|
|
Settings::configfile.add_options() |
|
|
|
this->configfile.add_options() |
|
|
|
; |
|
|
|
|
|
|
|
/*
|
|
|
|
* Call all custom register callbacks. |
|
|
|
* Get Callbacks object, then iterate over and call all register callbacks. |
|
|
|
*/ |
|
|
|
Callbacks* cb = mrmc::settings::Callbacks::getInstance(); |
|
|
|
while (cb->registerList.size() > 0) |
|
|
@ -154,46 +130,120 @@ Settings* Settings::instance(const int argc, const char* argv[], const char* fil |
|
|
|
RegisterCallback fptr = cb->registerList.front().second; |
|
|
|
cb->registerList.pop_front(); |
|
|
|
|
|
|
|
/*
|
|
|
|
* Allow modules to specify the type of an option. |
|
|
|
*/ |
|
|
|
switch (type) |
|
|
|
{ |
|
|
|
case CB_CONFIG: |
|
|
|
(*fptr)(Settings::configfile); |
|
|
|
(*fptr)(this->configfile); |
|
|
|
break; |
|
|
|
case CB_CLI: |
|
|
|
(*fptr)(Settings::commandline); |
|
|
|
(*fptr)(this->commandline); |
|
|
|
break; |
|
|
|
case CB_GENERIC: |
|
|
|
(*fptr)(Settings::generic); |
|
|
|
break; |
|
|
|
default: |
|
|
|
// hm. is this an error? can this actually happen with an enum?
|
|
|
|
(*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. |
|
|
|
* |
|
|
|
* Call all intermediate callbacks afterwards. |
|
|
|
*/ |
|
|
|
void Settings::firstRun(const int argc, const char* argv[], const char* filename) |
|
|
|
{ |
|
|
|
//! parse command line
|
|
|
|
bpo::store(bpo::command_line_parser(argc, argv).options(*(Settings::cli)).allow_unregistered().run(), this->vm); |
|
|
|
|
|
|
|
/*
|
|
|
|
* construct option descriptions for commandline and config file |
|
|
|
* load config file if specified |
|
|
|
*/ |
|
|
|
Settings::cli.add(Settings::commandline).add(generic); |
|
|
|
Settings::conf.add(Settings::configfile).add(generic); |
|
|
|
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 intermediate callbacks. |
|
|
|
*/ |
|
|
|
Settings::positional.add("trafile", 1); |
|
|
|
Settings::positional.add("labfile", 1); |
|
|
|
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 |
|
|
|
{ |
|
|
|
switch (type) |
|
|
|
{ |
|
|
|
case CB_CONFIG: |
|
|
|
(*fptr)(&this->configfile, this->vm); |
|
|
|
break; |
|
|
|
case CB_CLI: |
|
|
|
(*fptr)(&this->commandline, this->vm); |
|
|
|
break; |
|
|
|
case CB_GENERIC: |
|
|
|
(*fptr)(&this->generic, this->vm); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
catch (boost::bad_any_cast e) |
|
|
|
{ |
|
|
|
std::cerr << "An intermediate callback failed." << std::endl; |
|
|
|
std::cerr << e.what() << std::endl; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/*!
|
|
|
|
* Perform the second parser run: parse command line and config file (if |
|
|
|
* given) and check for unregistered options, requirements from |
|
|
|
* options_description objects and positional arguments. |
|
|
|
* |
|
|
|
* 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); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Actually create new instance |
|
|
|
* Call checker callbacks. |
|
|
|
*/ |
|
|
|
Settings::inst = new Settings(argc, argv, filename); |
|
|
|
Callbacks* cb = mrmc::settings::Callbacks::getInstance(); |
|
|
|
while (cb->checkerList.size() > 0) |
|
|
|
{ |
|
|
|
CheckerCallback fptr = cb->checkerList.front(); |
|
|
|
cb->checkerList.pop_front(); |
|
|
|
|
|
|
|
if (! (*fptr)(this->vm)) |
|
|
|
{ |
|
|
|
std::cerr << "Custom option checker failed." << std::endl; |
|
|
|
throw mrmc::exceptions::InvalidSettings(); |
|
|
|
} |
|
|
|
} |
|
|
|
return Settings::inst; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Print a short general usage information consisting of the positional |
|
|
|
* 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) |
|
|
|
{ |
|
|
|
os << "Usage: <binary> [options] <transition file> <label file>" << std::endl; |
|
|
|
os << Settings::cli << std::endl;; |
|
|
|
os << *(mrmc::settings::Settings::cli) << std::endl;; |
|
|
|
return os; |
|
|
|
} |
|
|
|
|
|
|
@ -216,17 +266,9 @@ std::ostream& help(std::ostream& os) |
|
|
|
*/ |
|
|
|
std::ostream& helpConfigfile(std::ostream& os) |
|
|
|
{ |
|
|
|
os << Settings::conf << std::endl;; |
|
|
|
os << *(mrmc::settings::Settings::conf) << std::endl;; |
|
|
|
return os; |
|
|
|
} |
|
|
|
|
|
|
|
/*!
|
|
|
|
* @return current instance. |
|
|
|
*/ |
|
|
|
Settings* instance() |
|
|
|
{ |
|
|
|
return Settings::inst; |
|
|
|
} |
|
|
|
|
|
|
|
} // namespace settings
|
|
|
|
} // namespace mrmc
|