diff --git a/dune/common/parametertreeparser.cc b/dune/common/parametertreeparser.cc index 5f9e685cd50baea1fc840ad51d8f04e890ea801d..0c6e559db42ae14c2e62e459ef717fc47b284290 100644 --- a/dune/common/parametertreeparser.cc +++ b/dune/common/parametertreeparser.cc @@ -14,8 +14,8 @@ #include <sstream> #include <fstream> #include <set> - -#include <dune/common/exceptions.hh> +#include <map> +#include <algorithm> std::string Dune::ParameterTreeParser::ltrim(const std::string& s) { @@ -132,7 +132,6 @@ void Dune::ParameterTreeParser::readINITree(std::istream& in, } - void Dune::ParameterTreeParser::readOptions(int argc, char* argv [], ParameterTree& pt) { @@ -158,3 +157,92 @@ void Dune::ParameterTreeParser::readOptions(int argc, char* argv [], } } + +void Dune::ParameterTreeParser::readNamedOptions(int argc, char* argv[], + ParameterTree& pt, + std::vector<std::string> keywords, + unsigned int required, + bool allow_more, + bool overwrite, + std::vector<std::string> help) +{ + std::string helpstr = generateHelpString(argv[0], keywords, required, help); + std::vector<bool> done(keywords.size(),false); + std::size_t current = 0; + + for (std::size_t i=1; i<std::size_t(argc); i++) + { + std::string opt = argv[i]; + // check for help + if (opt == "-h" || opt == "--help") + DUNE_THROW(HelpRequest, helpstr); + // is this a named parameter? + if (opt.substr(0,2) == "--") + { + size_t pos = opt.find('=',2); + if (pos == std::string::npos) + DUNE_THROW(ParameterTreeParserError, + "value missing for parameter " << opt << "\n" << helpstr); + std::string key = opt.substr(2,pos-2); + std::string value = opt.substr(pos+1,opt.size()-pos-1); + auto it = std::find(keywords.begin(), keywords.end(), key); + // is this param in the keywords? + if (!allow_more && it == keywords.end()) + DUNE_THROW(ParameterTreeParserError, + "unknown parameter " << key << "\n" << helpstr); + // do we overwrite an existing entry? + if (!overwrite && pt[key] != "") + DUNE_THROW(ParameterTreeParserError, + "parameter " << key << " already specified" << "\n" << helpstr); + pt[key] = value; + done[std::distance(keywords.begin(),it)] = true; // mark key as stored + } + else { + // map to the next keyword in the list + while(current < done.size() && done[current]) ++current; + // are there keywords left? + if (current >= done.size()) + DUNE_THROW(ParameterTreeParserError, + "superfluous unnamed parameter" << "\n" << helpstr); + // do we overwrite an existing entry? + if (!overwrite && pt[keywords[current]] != "") + DUNE_THROW(ParameterTreeParserError, + "parameter " << keywords[current] << " already specified" << "\n" << helpstr); + pt[keywords[current]] = opt; + done[current] = true; // mark key as stored + } + } + // check that we receive all required keywords + std::string missing = ""; + for (unsigned int i=0; i<keywords.size(); i++) + if ((i < required) && ! done[i]) // is this param required? + missing += std::string(" ") + keywords[i]; + if (missing.size()) + DUNE_THROW(ParameterTreeParserError, + "missing parameter(s) ... " << missing << "\n" << helpstr); +} + +std::string Dune::ParameterTreeParser::generateHelpString( + std::string progname, std::vector<std::string> keywords, unsigned int required, std::vector<std::string> help) +{ + static const char braces[] = "<>[]"; + std::string helpstr = ""; + helpstr = helpstr + "Usage: " + progname; + for (std::size_t i=0; i<keywords.size(); i++) + { + bool req = (i < required); + helpstr = helpstr + + " " + braces[req*2] + + keywords[i] +braces[req*2+1]; + } + helpstr = helpstr + "\n" + "Options:\n" + "-h / --help: this help\n"; + for (std::size_t i=0; i<std::min(keywords.size(),help.size()); i++) + { + if (help[i] != "") + helpstr = helpstr + "-" + + keywords[i] + ":\t" + help[i] + "\n"; + } + return helpstr; +} diff --git a/dune/common/parametertreeparser.hh b/dune/common/parametertreeparser.hh index bfa84ff685f58a7460f8ffe2f83f51b2af772622..31364b70fdff84ce6d5b35c1da0a4fe9cdf81e3f 100644 --- a/dune/common/parametertreeparser.hh +++ b/dune/common/parametertreeparser.hh @@ -9,11 +9,16 @@ #include <istream> #include <string> +#include <vector> #include <dune/common/parametertree.hh> +#include <dune/common/exceptions.hh> namespace Dune { + class ParameterTreeParserError : public RangeError {}; + class HelpRequest : public Exception {}; + /** \brief Parsers to set up a ParameterTree from various input sources * \ingroup Common * @@ -75,7 +80,7 @@ namespace Dune { * Parses C++ stream and build hierarchical config structure. * * \param in The stream to parse - * \param pt The parameter tree to store the config structure. + * \param[out] pt The parameter tree to store the config structure. * \param overwrite Whether to overwrite already existing values. * If false, values in the stream will be ignored * if the key is already present. @@ -93,7 +98,7 @@ namespace Dune { * Parses C++ stream and build hierarchical config structure. * * \param in The stream to parse - * \param pt The parameter tree to store the config structure. + * \param[out] pt The parameter tree to store the config structure. * \param srcname Name of the configuration source for error * messages, "stdin" or a filename. * \param overwrite Whether to overwrite already existing values. @@ -110,7 +115,7 @@ namespace Dune { * Parses file with given name and build hierarchical config structure. * * \param file filename - * \param pt The parameter tree to store the config structure. + * \param[out] pt The parameter tree to store the config structure. * \param overwrite Whether to overwrite already existing values. * If false, values in the stream will be ignored * if the key is already present. @@ -128,10 +133,37 @@ namespace Dune { * * \param argc arg count * \param argv arg values - * \param pt The parameter tree to store the config structure. + * \param[out] pt The parameter tree to store the config structure. */ static void readOptions(int argc, char* argv [], ParameterTree& pt); + /** + * \brief read [named] command line options and build hierarchical ParameterTree structure + * + * Similar to pythons named options we expect the parameters in the + * ordering induced by keywords, but allow the user to pass named options + * in the form of --key=value. Optionally the user can pass an additional + * vector with help strings. + * + * \param argc arg count + * \param argv arg values + * \param[out] pt The parameter tree to store the config structure. + * \param keywords vector with keywords names + * \param required number of required options (the first n keywords are required, default is all are required) + * \param allow_more allow more options than these listed in keywords (default = true) + * \param overwrite allow to overwrite existing options (default = true) + * \param help vector containing help strings + */ + static void readNamedOptions(int argc, char* argv[], + ParameterTree& pt, + std::vector<std::string> keywords, + unsigned int required = std::numeric_limits<unsigned int>::max(), + bool allow_more = true, + bool overwrite = true, + std::vector<std::string> help = std::vector<std::string>()); + + private: + static std::string generateHelpString(std::string progname, std::vector<std::string> keywords, unsigned int required, std::vector<std::string> help); }; } // end namespace Dune