Browse Source

Merge branch 'master' of https://sselab.de/lab9/private/git/MRMC

tempestpy_adaptions
dehnert 12 years ago
parent
commit
467988f97d
  1. 5
      src/mrmc.cpp
  2. 184
      src/utility/settings.cpp
  3. 307
      src/utility/settings.h

5
src/mrmc.cpp

@ -87,11 +87,6 @@ int main(const int argc, const char* argv[]) {
delete s;
return 0;
}
if (s->isSet("help-config")) {
std::cout << mrmc::settings::helpConfigfile;
delete s;
return 0;
}
if (s->isSet("test-prctl")) {
mrmc::parser::PRCTLParser parser(s->getString("test-prctl").c_str());
delete s;

184
src/utility/settings.cpp

@ -11,6 +11,8 @@
#include "log4cplus/loggingmacros.h"
extern log4cplus::Logger logger;
#include <boost/algorithm/string/join.hpp>
namespace mrmc {
namespace settings {
@ -19,11 +21,12 @@ namespace bpo = boost::program_options;
/*
* static initializers
*/
std::unique_ptr<bpo::options_description> mrmc::settings::Settings::cli;
std::unique_ptr<bpo::options_description> mrmc::settings::Settings::conf = nullptr;
std::unique_ptr<bpo::options_description> mrmc::settings::Settings::desc = nullptr;
std::string mrmc::settings::Settings::binaryName = "";
mrmc::settings::Settings* mrmc::settings::Settings::inst = nullptr;
std::map< std::pair<std::string, std::string>, bpo::options_description* > mrmc::settings::Settings::modules;
/*!
* The constructor fills the option descriptions, parses the
* command line and the config file and puts the option values to
@ -38,46 +41,63 @@ mrmc::settings::Settings* mrmc::settings::Settings::inst = nullptr;
* @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")
{
Settings::binaryName = std::string(argv[0]);
try
{
//! Initially fill description objects and call register callbacks
// Initially fill description objects
this->initDescriptions();
//! Take care of positional arguments
// Take care of positional arguments
Settings::positional.add("trafile", 1);
Settings::positional.add("labfile", 1);
// Check module triggers, add corresponding options
std::map< std::string, std::list< std::string > > options;
//! Create and fill collecting options descriptions
Settings::cli = std::unique_ptr<bpo::options_description>(new bpo::options_description());
Settings::cli->add(Settings::commandline).add(generic);
Settings::conf = std::unique_ptr<bpo::options_description>(new bpo::options_description());
Settings::conf->add(Settings::configfile).add(generic);
for (auto it : Settings::modules)
{
options[it.first.first].push_back(it.first.second);
}
for (auto it : options)
{
std::stringstream str;
str << "select " << it.first << " module (" << boost::algorithm::join(it.second, ", ") << ")";
Settings::desc->add_options()
(it.first.c_str(), bpo::value<std::string>(), str.str().c_str())
;
}
//! Perform first parse run and call intermediate callbacks
// Perform first parse run
this->firstRun(argc, argv, filename);
//! Rebuild collecting options descriptions
Settings::cli = std::unique_ptr<bpo::options_description>(new bpo::options_description());
Settings::cli->add(Settings::commandline).add(generic);
Settings::conf = std::unique_ptr<bpo::options_description>(new bpo::options_description());
Settings::conf->add(Settings::configfile).add(generic);
// Check module triggers
for (auto it : Settings::modules)
{
std::pair< std::string, std::string > trigger = it.first;
if (this->vm.count(trigger.first))
{
if (this->vm[trigger.first].as<std::string>().compare(trigger.second) == 0)
{
Settings::desc->add(*it.second);
Settings::modules.erase(trigger);
}
}
}
//! Stop if help is set
if ((this->vm.count("help") > 0) || (this->vm.count("help-config") > 0))
// Stop if help is set
if (this->vm.count("help") > 0)
{
return;
}
//! Perform second run and call checker callbacks
// Perform second run
this->secondRun(argc, argv, filename);
//! Finalize parsed options, check for specified requirements
// Finalize parsed options, check for specified requirements
bpo::notify(this->vm);
mrmc::settings::Callbacks::instance()->disabled = true;
LOG4CPLUS_DEBUG(logger, "Finished loading config.");
}
catch (bpo::reading_file e)
@ -118,100 +138,38 @@ Settings::Settings(const int argc, const char* argv[], const char* filename)
void Settings::initDescriptions()
{
LOG4CPLUS_DEBUG(logger, "Initializing descriptions.");
this->commandline.add_options()
Settings::desc = std::unique_ptr<bpo::options_description>(new bpo::options_description("Generic Options"));
Settings::desc->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")
;
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()
;
/*
* Get Callbacks object, then iterate over and call all register callbacks.
*/
Callbacks* cb = mrmc::settings::Callbacks::instance();
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.
*
* Call all intermediate callbacks afterwards.
*/
void Settings::firstRun(const int argc, const char* argv[], const char* filename)
{
LOG4CPLUS_DEBUG(logger, "Performing first run.");
//! parse command line
bpo::store(bpo::command_line_parser(argc, argv).options(*(Settings::cli)).allow_unregistered().run(), this->vm);
// parse command line
bpo::store(bpo::command_line_parser(argc, argv).options(*(Settings::desc)).allow_unregistered().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);
bpo::store(bpo::parse_config_file<char>(this->vm["configfile"].as<std::string>().c_str(), *(Settings::desc)), this->vm, true);
}
else if (filename != NULL)
{
bpo::store(bpo::parse_config_file<char>(filename, *(Settings::conf)), this->vm, true);
}
/*
* Call intermediate callbacks.
*/
Callbacks* cb = mrmc::settings::Callbacks::instance();
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;
LOG4CPLUS_ERROR(logger, "An intermediate callback failed.\n" << e.what());
}
bpo::store(bpo::parse_config_file<char>(filename, *(Settings::desc)), this->vm, true);
}
}
@ -219,42 +177,22 @@ void Settings::firstRun(const int argc, const char* argv[], const char* filename
* 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)
{
LOG4CPLUS_DEBUG(logger, "Performing second run.");
//! Parse command line
bpo::store(bpo::command_line_parser(argc, argv).options(*(Settings::cli)).positional(this->positional).run(), this->vm);
// Parse command line
bpo::store(bpo::command_line_parser(argc, argv).options(*(Settings::desc)).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);
bpo::store(bpo::parse_config_file<char>(this->vm["configfile"].as<std::string>().c_str(), *(Settings::desc)), this->vm, true);
}
else if (filename != NULL)
{
bpo::store(bpo::parse_config_file<char>(filename, *(Settings::conf)), this->vm, true);
}
/*
* Call checker callbacks.
*/
Callbacks* cb = mrmc::settings::Callbacks::instance();
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;
LOG4CPLUS_ERROR(logger, "A checker callback returned false.");
throw mrmc::exceptions::InvalidSettings();
}
bpo::store(bpo::parse_config_file<char>(filename, *(Settings::desc)), this->vm, true);
}
}
@ -269,19 +207,11 @@ void Settings::secondRun(const int argc, const char* argv[], const char* filenam
std::ostream& help(std::ostream& os)
{
os << "Usage: " << mrmc::settings::Settings::binaryName << " [options] <transition file> <label file>" << std::endl;
os << *(mrmc::settings::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 << *(mrmc::settings::Settings::conf) << std::endl;;
os << *(mrmc::settings::Settings::desc) << std::endl;
for (auto it : Settings::modules)
{
os << *(it.second) << std::endl;
}
return os;
}

307
src/utility/settings.h

@ -9,6 +9,7 @@
#define SETTINGS_H_
#include <iostream>
#include <sstream>
#include <list>
#include <utility>
#include <memory>
@ -25,14 +26,9 @@ namespace mrmc {
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.
* @brief 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.
@ -44,36 +40,15 @@ namespace settings {
* @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.
* option modules. An option module can be anything that implements the
* interface specified by registerModule().
*/
class Settings
{
public:
/*!
* @brief Get value of a generic option.
* @brief Get value of a generic option.
*/
template <typename T>
const T& get(const std::string &name) const {
@ -82,18 +57,68 @@ namespace settings {
}
/*!
* @brief Get value of string option
* @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
* @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.
*
* A new settings module can be registered via
* @code
* mrmc::settings::Settings::registerModule<mrmc::ModuleClass>();
* @endcode
* This has to be done before any parsing takes place, i.e. before newInstance() is called.
*
* 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
*
* The semantic is the following:
* If the trigger <a,b> is true, i.e. if option a is set to b,
* the options_description object will be added to the internal
* option object.
*
* Furthermore, it will generate the option specified by the
* trigger.
*
* The functions could look like this:
* @code
* static std::string getModuleName() { return "Backend A"; }
* static std::pair<std::string, std::string> getOptionTrigger() {
* return std::pair<std::string, std::string>("backend", "a");
* }
* static void putOptions(boost::program_options::options_description* desc) {
* desc->add_options()("foo", "bar");
* }
* @endcode
*/
template <typename T>
static void registerModule()
{
// get trigger
std::pair< std::string, std::string > trigger = T::getOptionTrigger();
// build description name
std::stringstream str;
str << T::getModuleName() << " (" << trigger.first << " = " << trigger.second << ")";
bpo::options_description* desc = new bpo::options_description(str.str());
// but options
T::putOptions(desc);
// store
Settings::modules[ trigger ] = desc;
}
friend std::ostream& help(std::ostream& os);
friend std::ostream& helpConfigfile(std::ostream& os);
@ -121,31 +146,20 @@ namespace settings {
*/
void secondRun(const int argc, const char* argv[], const char* filename);
/*!
* @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.
* @brief Collecting option descriptions.
*/
static std::unique_ptr<bpo::options_description> cli;
static std::unique_ptr<bpo::options_description> desc;
/*!
* @brief Collecting option descriptions for config file.
* @brief Contains option descriptions for all modules.
*/
static std::unique_ptr<bpo::options_description> conf;
static std::map< std::pair< std::string, std::string >, bpo::options_description* > modules;
/*!
* @brief option mapping.
@ -160,7 +174,7 @@ namespace settings {
/*!
* @brief actual instance of this class.
*/
static Settings* inst;
static Settings* inst;
};
/*!
@ -168,11 +182,6 @@ namespace settings {
*/
std::ostream& help(std::ostream& os);
/*!
* @brief Print help for config file options.
*/
std::ostream& helpConfigfile(std::ostream& os);
/*!
* @brief Return current instance.
*
@ -199,195 +208,7 @@ namespace settings {
Settings::inst = new Settings(argc, argv, filename);
return Settings::inst;
}
/*!
* @brief Function type for functions registering new options.
*/
typedef void(*RegisterCallback)(bpo::options_description&);
/*!
* @brief Function type for functions changing the parser state
* between the first and second run.
*/
typedef void(*IntermediateCallback)(bpo::options_description*, bpo::variables_map&);
/*!
* @brief Function type for function checking constraints on settings.
*/
typedef bool(*CheckerCallback)(bpo::variables_map&);
/*!
* @brief This enums specifies the three types of options.
*/
enum CallbackType {
//! Option can be set in config file
CB_CONFIG,
//! Option can be set on command line
CB_CLI,
//! Option can be set in config file and command line
CB_GENERIC
};
/*!
* @brief This class handles callbacks for registering new options and
* checking constraints on them afterwards.
*
* As it should never be used directly, but only through the Register
* class, it does not provide any public methods.
*
* This class is also a singleton (like Settings) and is implemented much
* simpler as we don't need any custom initialization code.
*/
class Callbacks
{
public:
inline void put(const CallbackType type, const RegisterCallback ptr)
{
if (this->disabled) throw mrmc::exceptions::InvalidSettings();
this->registerList.push_back(std::pair<CallbackType, RegisterCallback>(type, ptr));
}
inline void put(const CallbackType type, const IntermediateCallback ptr)
{
if (this->disabled) throw mrmc::exceptions::InvalidSettings();
this->intermediateList.push_back(std::pair<CallbackType, IntermediateCallback>(type, ptr));
}
inline void put(const CheckerCallback ptr)
{
if (this->disabled) throw mrmc::exceptions::InvalidSettings();
this->checkerList.push_back(ptr);
}
private:
/*!
* @brief Stores register callbacks.
*/
std::list<std::pair<CallbackType, RegisterCallback>> registerList;
/*!
* @brief Stores intermediate callbacks.
*/
std::list<std::pair<CallbackType, IntermediateCallback>> intermediateList;
/*!
* @brief Stores check callbacks.
*/
std::list<CheckerCallback> checkerList;
/*!
* @brief Stores if we already loaded the settings.
*/
bool disabled;
/*!
* @brief Private constructor.
*/
Callbacks() : disabled(false) {}
/*!
* @brief Private copy constructor.
*/
Callbacks(const Callbacks&) {}
/*!
* @brief Private destructor.
*/
~Callbacks() {}
/*!
* @brief Returns current instance to create singleton.
* @return current instance
*/
inline static Callbacks* instance()
{
static Callbacks inst;
return &inst;
}
/*!
* @brief Register class needs access to lists.
*/
friend class Register;
/*!
* @brief Settings class need access to lists.
*/
friend class Settings;
};
/*!
* @brief Wrapper class to allow for registering callbacks during
* static initialization.
*
* To use this class, use the following includes:
* @code
* #include "src/utility/settings.h"
* #include <boost/program_options.hpp>
* namespace bpo = boost::program_options;
* @endcode
*/
class Register
{
public:
/*!
* @brief Registers given function as register callback.
*
* This constructor registers a callback routine that might add
* new options for the Settings class. It should be used like
* this:
* @code
* void register(bpo::options_description& desc) {
* // do something with desc here
* }
* mrmc::settings::Register reg(mrmc::settings::CB_CLI, &register);
* @endcode
* This code should be executed during static initialization, i.e.
* it should be somewhere in the cpp-file.
*/
Register(const CallbackType type, const RegisterCallback ptr)
{
mrmc::settings::Callbacks::instance()->put(type, ptr);
}
/*!
* @brief Registers given function as intermediate callback.
*
* This constructor registers a callback routine that can check
* the option assignment after the first run and change the
* options description before the second run.
* It should be used like this:
* @code
* void intermediate(bpo::options_description& desc, bpo::variables_map& map) {
* // check contents of map and maybe change desc
* }
* mrmc::settings::Register reg(mrmc::settings::CB_CLI, &intermediate);
* @endcode
* This code should be executed during static initialization, i.e.
* it should be somewhere in the cpp-file.
*/
Register(const CallbackType type, const IntermediateCallback ptr)
{
mrmc::settings::Callbacks::instance()->put(type, ptr);
}
/*!
* @brief Registers given function as check callback.
*
* This constructor registers a callback routine that can check
* the option assignment after the Settings class has loaded
* them. It should be used like this:
* @code
* void check(bpo::variables_map& map) {
* // check contents of map
* }
* mrmc::settings::Register reg(&check);
* @endcode
* This code should be executed during static initialization, i.e.
* it should be somewhere in the cpp-file.
*/
Register(const CheckerCallback ptr)
{
mrmc::settings::Callbacks::instance()->put(ptr);
}
};
} // namespace settings
} // namespace mrmc

Loading…
Cancel
Save