Browse Source

replace callbacks by register methods

tempestpy_adaptions
gereon 12 years ago
parent
commit
09b35a2fa1
  1. 147
      src/utility/settings.cpp
  2. 233
      src/utility/settings.h

147
src/utility/settings.cpp

@ -19,11 +19,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,7 +39,6 @@ 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
@ -49,22 +49,33 @@ Settings::Settings(const int argc, const char* argv[], const char* filename)
//! Take care of positional arguments
Settings::positional.add("trafile", 1);
Settings::positional.add("labfile", 1);
//! Check module triggers
for (auto it : Settings::modules)
{
std::pair< std::string, std::string > trigger = it.first;
Settings::desc->add_options()
(trigger.first.c_str(), bpo::value<std::string>(), trigger.second.c_str())
;
}
//! 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);
//! 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))
@ -77,7 +88,6 @@ Settings::Settings(const int argc, const char* argv[], const char* filename)
//! 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,43 +128,16 @@ 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;
}
}
}
/*!
@ -168,50 +151,18 @@ 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);
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);
}
}
@ -226,35 +177,17 @@ void Settings::secondRun(const int argc, const char* argv[], const char* filenam
{
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);
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,7 +202,13 @@ 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;
os << *(mrmc::settings::Settings::desc) << std::endl;
for (auto it : Settings::modules)
{
std::pair< std::string, std::string > trigger = it.first;
os << "If --" << trigger.first << " = " << trigger.second << ":" << std::endl;
os << *(it.second) << std::endl;
}
return os;
}
@ -281,7 +220,7 @@ std::ostream& help(std::ostream& os)
*/
std::ostream& helpConfigfile(std::ostream& os)
{
os << *(mrmc::settings::Settings::conf) << std::endl;;
os << *(mrmc::settings::Settings::desc) << std::endl;;
return os;
}

233
src/utility/settings.h

@ -25,6 +25,7 @@ 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
@ -73,7 +74,7 @@ namespace 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 +83,33 @@ 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
*
* This function implicitly defines the following interface for any SettingsModule:
* @code
* static std::pair< std::string, std::string> getSettings(boost::program_options::options_description*);
* @endcode
*/
template <typename T>
static void registerModule()
{
bpo::options_description* desc = new bpo::options_description();
Settings::modules[ T::getSettings(desc) ] = desc;
}
friend std::ostream& help(std::ostream& os);
friend std::ostream& helpConfigfile(std::ostream& os);
@ -121,31 +137,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 +165,7 @@ namespace settings {
/*!
* @brief actual instance of this class.
*/
static Settings* inst;
static Settings* inst;
};
/*!
@ -199,195 +204,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