ArgParse choices implemented

master
Denes Matetelki 13 years ago
parent 3920051e6f
commit dba6b1c91d

@ -4,7 +4,7 @@
#include <string> #include <string>
#include <list> #include <list>
#include <map> #include <map>
#include <stdexcept> #include <set>
// Aims for the functionality of Python argparse // Aims for the functionality of Python argparse
// http://docs.python.org/library/argparse.html#module-argparse // http://docs.python.org/library/argparse.html#module-argparse
@ -14,11 +14,11 @@ class ArgParse
public: public:
enum ValueType { enum ValueType {
TYPE_NONE, NONE,
TYPE_STRING, STRING,
TYPE_INT, INT,
TYPE_DOUBLE, DOUBLE,
TYPE_BOOL BOOL
}; };
enum ValueRequired { enum ValueRequired {
@ -32,16 +32,15 @@ public:
void addArgument(const std::string name, // "-f,--foo" void addArgument(const std::string name, // "-f,--foo"
const std::string help, const std::string help,
const enum ValueType type = TYPE_NONE, const enum ValueType type = NONE,
const enum ValueRequired valueRequired = REQUIRED, const enum ValueRequired valueRequired = REQUIRED,
const std::string valueName = std::string(""), const std::string valueName = std::string(""),
const std::string choices = std::string("")); // "yes,no" or range: [1..100] const std::string choices = std::string("")); // {"yes,no"} or range: {1..100}
// throw(std::runtime_error)
bool parseArgs(const int argc, bool parseArgs(const int argc,
const char* argv[]) throw(std::runtime_error); const char* argv[]);
bool parseArgs(const std::list<std::string> argList);
bool parseArgs(const std::list<std::string> argList) throw(std::runtime_error);
// is this arg in the map of understood arguments? // is this arg in the map of understood arguments?
bool isArg(const std::string arg) const; bool isArg(const std::string arg) const;
@ -73,30 +72,32 @@ private:
bool m_valueHasBeenSet; bool m_valueHasBeenSet;
Argument (const std::string help, Argument (const std::string help,
const enum ValueType type = TYPE_NONE, const enum ValueType type = NONE,
const enum ValueRequired valueRequired = REQUIRED, const enum ValueRequired valueRequired = REQUIRED,
const std::string valueName = std::string(""), const std::string valueName = std::string(""),
const std::string choices = std::string(""), const std::string choices = std::string(""),
const std::string value = std::string("")) const std::string value = std::string(""));
: m_help(help) };
, m_type(type)
, m_valueRequired(valueRequired) class argCompare {
, m_valueName(valueName) public:
, m_choices(choices) // short and long arg shall be compared with same amount of dashes
, m_value(value) bool operator()(const std::string a,const std::string b) const;
, m_found(false)
, m_valueHasBeenSet(false) {};
}; };
typedef std::map<std::string, Argument, argCompare> ArgMap;
// arg is just the shor or long form: "-h" or "--help" // arg is just the shor or long form: "-h" or "--help"
std::map<std::string, Argument>::iterator findElement(const std::string param); ArgMap::iterator findElement(const std::string param);
std::set<std::string> parseCommaSepStringToSet(const std::string s) const;
std::string typeToString(const ValueType type, const std::string valueName) const;
std::string m_description; std::string m_description;
std::string m_epilog; std::string m_epilog;
std::string m_programName; std::string m_programName;
std::map<std::string, Argument> m_params; ArgMap m_params;
}; // class ArgParse }; // class ArgParse

@ -6,9 +6,14 @@
#include <algorithm> #include <algorithm>
#include <string> #include <string>
#include <set>
#include <stdexcept>
#include <stdio.h> #include <stdio.h>
ArgParse::ArgParse(const std::string description, ArgParse::ArgParse(const std::string description,
const std::string epilog, const std::string epilog,
const bool add_Help) const bool add_Help)
@ -29,13 +34,14 @@ void ArgParse::addArgument(const std::string arg,
const std::string valueName, const std::string valueName,
const std::string choices) const std::string choices)
{ {
Argument argument(help, type, valueRequired, valueName, choices, "");
Argument argument(help, type, valueRequired, typeToString(type, valueName), choices, "");
m_params.insert(std::pair<std::string, Argument>(arg, argument)); m_params.insert(std::pair<std::string, Argument>(arg, argument));
} }
bool ArgParse::parseArgs(const int argc, bool ArgParse::parseArgs(const int argc,
const char* argv[]) throw(std::runtime_error) const char* argv[])
{ {
std::list<std::string> argList; std::list<std::string> argList;
for (int i = 0; i < argc; ++i ) for (int i = 0; i < argc; ++i )
@ -45,7 +51,7 @@ bool ArgParse::parseArgs(const int argc,
} }
bool ArgParse::parseArgs(const std::list<std::string> argList) throw(std::runtime_error) bool ArgParse::parseArgs(const std::list<std::string> argList)
{ {
m_programName = argList.front(); m_programName = argList.front();
@ -54,19 +60,21 @@ bool ArgParse::parseArgs(const std::list<std::string> argList) throw(std::runtim
for (++it; it != argList.end(); ++it ) { for (++it; it != argList.end(); ++it ) {
if ( (*it).at(0) != '-' ) if ( (*it).at(0) != '-' )
throw std::runtime_error(std::string(*it).append(" shall start with a dash.")); throw std::runtime_error(std::string(*it).
append(" shall start with a dash."));
// inspect each arument // inspect each arument
std::map<std::string, Argument>::iterator it2 = findElement(*it); ArgMap::iterator it2 = findElement(*it);
if ( it2 == m_params.end() ) if ( it2 == m_params.end() )
throw std::runtime_error(std::string(*it).append(" is not known.")); throw std::runtime_error(std::string(*it).append(" is not known."));
if ( (*it2).second.m_found ) if ( (*it2).second.m_found )
throw std::runtime_error(std::string(*it).append(" has been given before.")); throw std::runtime_error(std::string(*it).
append(" has been given before."));
(*it2).second.m_found = true; (*it2).second.m_found = true;
if ( (*it2).second.m_type == TYPE_NONE ) if ( (*it2).second.m_type == NONE )
continue; continue;
std::list<std::string>::const_iterator next = it; std::list<std::string>::const_iterator next = it;
@ -74,66 +82,125 @@ bool ArgParse::parseArgs(const std::list<std::string> argList) throw(std::runtim
if ( next == argList.end() ) { if ( next == argList.end() ) {
if ( (*it2).second.m_valueRequired == REQUIRED ) if ( (*it2).second.m_valueRequired == REQUIRED )
throw std::runtime_error(std::string(*it).append(" requires a parameter.")); throw std::runtime_error(std::string(*it).
append(" requires a parameter."));
if ( (*it2).second.m_valueRequired == OPTIONAL ) if ( (*it2).second.m_valueRequired == OPTIONAL )
continue; continue;
} }
if ( (*it2).second.m_valueRequired == OPTIONAL && findElement( *next ) != m_params.end() ) if ( (*it2).second.m_valueRequired == OPTIONAL &&
findElement( *next ) != m_params.end() )
continue; continue;
switch ( (*it2).second.m_type ) { switch ( (*it2).second.m_type ) {
case TYPE_INT : { case INT : {
int temp; int temp;
if ( sscanf( next->c_str(), "%d", &temp ) == 0 ) if ( sscanf( next->c_str(), "%d", &temp ) == 0 )
throw std::runtime_error(std::string( *next ). throw std::runtime_error(std::string( *next ).
append(" is not an integer, required by ").append(*it)); append(" is not an integer, required by ").append( *it ));
break;
} if ( !(*it2).second.m_choices.empty() ) {
case TYPE_DOUBLE : { int lowerBound;
double temp; int upperBound;
if ( sscanf( next->c_str(), "%f", &temp ) == 0 ) if ( sscanf( (*it2).second.m_choices.c_str(),
throw std::runtime_error(std::string( *next ). "%d..%d", &lowerBound, &upperBound ) != 2 )
append(" is not a double, required by ").append(*it)); throw std::logic_error(std::string( *it ).
break; append(" has syntax error. ").
} append("Range expected in a INT..INT format" ));
case TYPE_BOOL : {
std::string temp = *next; if ( temp < lowerBound || temp > upperBound )
std::transform(temp.begin(), temp.end(),temp.begin(), ::toupper); throw std::runtime_error(std::string( *it ).
if ( temp != "TRUE" && temp != "FALSE" ) append( " expects an integer in the range of {" ).
throw std::runtime_error(std::string( *next ). append( (*it2).second.m_choices).
append(" is not a boolean, required by ").append(*it)); append("}") );
break; }
break;
}
case DOUBLE : {
double temp;
if ( sscanf( next->c_str(), "%f", &temp ) == 0 )
throw std::runtime_error(std::string( *next ).
append(" is not a double, required by ").append(*it));
if ( !(*it2).second.m_choices.empty() ) {
double lowerBound;
double upperBound;
if ( sscanf( (*it2).second.m_choices.c_str(),
"%f..%f", &lowerBound, &upperBound ) != 2 )
throw std::logic_error(std::string( *it ).
append(" has syntax error. ").
append("Range expected in a DOUBLE..DOUBLE format" ));
if ( temp < lowerBound || temp > upperBound )
throw std::runtime_error(std::string( *it ).
append( " expects a double in the range of [" ).
append( (*it2).second.m_choices).
append("}") );
}
break;
}
case BOOL : {
std::string temp = *next;
std::transform(temp.begin(), temp.end(),temp.begin(), ::toupper);
if ( temp != "TRUE" && temp != "FALSE" )
throw std::runtime_error(std::string( *next ).
append(" is not a boolean, required by ").append(*it));
if ( !(*it2).second.m_choices.empty() )
throw std::logic_error(std::string( *next ).
append(" expects a boolean not choices."));
break;
}
case STRING : {
if ( !(*it2).second.m_choices.empty() ) {
std::set<std::string> choices =
parseCommaSepStringToSet( (*it2).second.m_choices );
if ( choices.find( *next ) == choices.end() )
throw std::runtime_error(std::string( *next ).
append(" is not in the expected list of choices: {").
append( (*it2).second.m_choices ).
append("}"));
}
break;
}
default:
break;
} }
default:
break;
}
(*it2).second.m_value = *next; (*it2).second.m_value = *next;
(*it2).second.m_valueHasBeenSet = true; (*it2).second.m_valueHasBeenSet = true;
++it; ++it;
} }
return true; return true;
} }
bool ArgParse::isArg(const std::string arg) const bool ArgParse::isArg(const std::string arg) const
{ {
std::map<std::string, Argument>::const_iterator it = m_params.find(arg); ArgMap::const_iterator it = m_params.find(arg);
return it != m_params.end(); return it != m_params.end();
} }
bool ArgParse::foundArg(const std::string arg) const bool ArgParse::foundArg(const std::string arg) const
{ {
std::map<std::string, Argument>::const_iterator it = m_params.find(arg); ArgMap::const_iterator it = m_params.find(arg);
return it != m_params.end() && (*it).second.m_found == true; return it != m_params.end() && (*it).second.m_found == true;
} }
bool ArgParse::argHasValue(const std::string arg) const bool ArgParse::argHasValue(const std::string arg) const
{ {
std::map<std::string, Argument>::const_iterator it = m_params.find(arg); ArgMap::const_iterator it = m_params.find(arg);
return it != m_params.end() && return it != m_params.end() &&
(*it).second.m_found == true && (*it).second.m_found == true &&
(*it).second.m_valueHasBeenSet; (*it).second.m_valueHasBeenSet;
@ -145,7 +212,7 @@ bool ArgParse::argAsString(const std::string arg, std::string &value) const
if ( !argHasValue(arg) ) if ( !argHasValue(arg) )
return false; return false;
std::map<std::string, Argument>::const_iterator it = m_params.find(arg); ArgMap::const_iterator it = m_params.find(arg);
value = (*it).second.m_value; value = (*it).second.m_value;
return true; return true;
} }
@ -156,7 +223,7 @@ bool ArgParse::argAsInt(const std::string arg, int &value) const
if ( !argHasValue(arg) ) if ( !argHasValue(arg) )
return false; return false;
std::map<std::string, Argument>::const_iterator it = m_params.find(arg); ArgMap::const_iterator it = m_params.find(arg);
value = atoi((*it).second.m_value.c_str()); value = atoi((*it).second.m_value.c_str());
return true; return true;
} }
@ -167,7 +234,7 @@ bool ArgParse::argAsDouble(const std::string arg, double &value) const
if ( !argHasValue(arg) ) if ( !argHasValue(arg) )
return false; return false;
std::map<std::string, Argument>::const_iterator it = m_params.find(arg); ArgMap::const_iterator it = m_params.find(arg);
value = atof((*it).second.m_value.c_str()); value = atof((*it).second.m_value.c_str());
return true; return true;
} }
@ -178,7 +245,7 @@ bool ArgParse::argAsBool(const std::string arg, bool &value) const
if ( !argHasValue(arg) ) if ( !argHasValue(arg) )
return false; return false;
std::map<std::string, Argument>::const_iterator it = m_params.find(arg); ArgMap::const_iterator it = m_params.find(arg);
std::string temp = (*it).second.m_value; std::string temp = (*it).second.m_value;
std::transform(temp.begin(), temp.end(),temp.begin(), ::toupper); std::transform(temp.begin(), temp.end(),temp.begin(), ::toupper);
@ -199,17 +266,31 @@ std::string ArgParse::usage() const
ss << " [OPTION]" << std::endl << std::endl; ss << " [OPTION]" << std::endl << std::endl;
ss << "Options:" << std::endl; ss << "Options:" << std::endl;
int length; int length;
std::map<std::string, Argument>::const_iterator it; ArgMap::const_iterator it;
for ( it = m_params.begin(); it != m_params.end(); it++ ) { for ( it = m_params.begin(); it != m_params.end(); it++ ) {
ss << (*it).first; ss << (*it).first;
length = (*it).first.length(); length = (*it).first.length();
if ( (*it).second.m_type != TYPE_NONE ) { if ( (*it).second.m_type != NONE ) {
if ((*it).second.m_valueRequired == REQUIRED ) {
ss << "=" << (*it).second.m_valueName; ss << " ";
length += (*it).second.m_valueName.length()+1; length++;
if ( (*it).second.m_valueRequired == OPTIONAL ) {
ss << "[";
length ++;
}
if ( !(*it).second.m_choices.empty() ) {
ss << "{" << (*it).second.m_choices << "}";
length += (*it).second.m_choices.length() + 2;
} else { } else {
ss << "[=" << (*it).second.m_valueName << "]"; ss << (*it).second.m_valueName;
length += (*it).second.m_valueName.length() + 3; length += (*it).second.m_valueName.length();
}
if ( (*it).second.m_valueRequired == OPTIONAL ) {
ss << "]";
length++;
} }
} }
ss << std::string(30-length, ' '); ss << std::string(30-length, ' ');
@ -222,10 +303,43 @@ std::string ArgParse::usage() const
return ss.str();; return ss.str();;
} }
ArgParse::Argument::Argument (const std::string help,
const enum ValueType type,
const enum ValueRequired valueRequired,
const std::string valueName,
const std::string choices,
const std::string value)
: m_help(help)
, m_type(type)
, m_valueRequired(valueRequired)
, m_valueName(valueName)
, m_choices(choices)
, m_value(value)
, m_found(false)
, m_valueHasBeenSet(false)
{
}
bool
ArgParse::argCompare::operator()(const std::string a,const std::string b) const
{
if ( a.at(1) == '-' && b.at(1) != '-' )
return a.substr(1) < b;
if ( b.at(1) == '-' && a.at(1) != '-' )
return a < b.substr(1);
return a<b;
}
std::map<std::string, ArgParse::Argument>::iterator std::map<std::string, ArgParse::Argument>::iterator
ArgParse::findElement(const std::string param) ArgParse::findElement(const std::string param)
{ {
std::map<std::string, Argument>::iterator it; ArgMap::iterator it;
for( it = m_params.begin(); it != m_params.end(); ++it) { for( it = m_params.begin(); it != m_params.end(); ++it) {
// if it's the short param at the beginning // if it's the short param at the beginning
@ -241,3 +355,54 @@ ArgParse::findElement(const std::string param)
return m_params.end(); return m_params.end();
} }
std::set<std::string>
ArgParse::parseCommaSepStringToSet(const std::string s) const
{
std::string tmp(s);
std::set<std::string> stringSet;
size_t pos;
std::string element;
while ( !tmp.empty() ) {
pos = tmp.find(',');
if (pos == std::string::npos) {
element = tmp;
tmp = "";
} else {
element = tmp.substr(0,pos);
tmp = tmp.substr(pos+1);
}
// if ( element.empty() ) ... ?
if ( stringSet.find(element) != stringSet.end() ) {
throw std::logic_error( std::string( element ).
append(" listed twice in ").append(s) );
}
stringSet.insert(element);
}
return stringSet;
}
std::string ArgParse::typeToString(const ValueType type, std::string valueName) const
{
if ( type != NONE && valueName.empty() ) {
switch ( type ) {
case INT :
return "INT";
case DOUBLE :
return "DOUBLE";
break;
case BOOL :
return "BOOL";
break;
default:
return "";
}
}
return valueName;
}
Loading…
Cancel
Save