Working Parser, Mate !

This commit is contained in:
Jerome 2021-12-11 20:21:08 +01:00
parent 1cb3ecd9a0
commit f80b9b9b0e
6 changed files with 237 additions and 68 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
build/*
argParseCpp
argParseTest.exe
*.bat

View file

@ -55,12 +55,29 @@ class ParserParam
/// Returns the help entry string : "-p --param value Parameter "param" with value "value_param" of type integer number" /// Returns the help entry string : "-p --param value Parameter "param" with value "value_param" of type integer number"
std::string GetHelpEntryStr() const; std::string GetHelpEntryStr() const;
/// Parses the argument(s) given, according to the parameter type. Returns true if the parsing was successful.
bool Parse(const std::string & arg, std::string argval = "");
/// Returns true if the provided name corresponds to either the short name or the long name, even if the parameter is of type "equal"
bool IsThisParamNamed(const std::string & name) const;
/// Returns whether the parameter's value is valid or not
bool IsValid() const;
public: public:
std::string shortName; std::string shortName;
std::string longName; std::string longName;
ParameterType type; ParameterType type;
ParameterValueType valueType; ParameterValueType valueType;
std::string docString; std::string docString;
bool validity; //! indicates whether the parameter's value is valid or not
// Storage for all types of values the parameter can have
bool boolVal;
int intVal;
double doubleVal;
std::string stringVal;
}; };
/// This class implements a simple argument parser in C++. It can be used to parse a variety of input arguments to programs. /// This class implements a simple argument parser in C++. It can be used to parse a variety of input arguments to programs.
@ -74,13 +91,13 @@ class Parser
void AddParameter(ParserParam param); void AddParameter(ParserParam param);
/// Parses the arguments passed as parameters. /// Parses the arguments passed as parameters.
void Parse(int argc, char **argv); bool Parse(int argc, char **argv);
/// Parses the arguments passed as a single string. /// Parses the arguments passed as a single string.
void Parse(const std::string & argString); bool Parse(const std::string & argString);
/// Parses the arguments passed as parameters. /// Parses the arguments passed as parameters.
void Parse(std::vector<std::string> args); bool Parse(std::vector<std::string> args);
/// Prints the custom help message if it has been defined, or an automatically constructed one. /// Prints the custom help message if it has been defined, or an automatically constructed one.
void PrintHelp() const; void PrintHelp() const;
@ -88,11 +105,11 @@ class Parser
/// Prints the parsed arguments and their associated values. /// Prints the parsed arguments and their associated values.
void PrintParsedArguments() const; void PrintParsedArguments() const;
/// Returns the value of the specified argument. A conversion is done between the input text and the output value type. /// Returns the value of the specified argument.
template<typename T> T GetArgumentValue(const std::string & argName) bool GetArgumentValueBool(const std::string & argName);
{ int GetArgumentValueInt(const std::string & argName);
double GetArgumentValueDouble(const std::string & argName);
} std::string const& GetArgumentValueString(const std::string & argName);
protected: protected:
/// Cleans the argument list passed as parameter. /// Cleans the argument list passed as parameter.
@ -100,12 +117,6 @@ class Parser
protected: protected:
std::vector<ParserParam> parserParams; std::vector<ParserParam> parserParams;
std::map<std::string, bool> boolParameters;
std::map<std::string, int> intParameters;
std::map<std::string, double> doubleParameters;
std::map<std::string, std::string> stringParameters;
std::string customHelpMessage; std::string customHelpMessage;
}; };

View file

@ -7,4 +7,10 @@
#define PRINT_VAR(v); std::cout << #v << "\t = "; std::cout << (v) << "\n"; #define PRINT_VAR(v); std::cout << #v << "\t = "; std::cout << (v) << "\n";
#define PRINT_VEC(v); std::cout << #v << "\t =\n"; for(unsigned int i_PRINT_VEC = 0 ; i_PRINT_VEC < (v).size() ; i_PRINT_VEC++) { std::cout << (v)[i_PRINT_VEC] << "\n"; } #define PRINT_VEC(v); std::cout << #v << "\t =\n"; for(unsigned int i_PRINT_VEC = 0 ; i_PRINT_VEC < (v).size() ; i_PRINT_VEC++) { std::cout << (v)[i_PRINT_VEC] << "\n"; }
/// Returns true if the string str contains the character c.
bool StringContains(const std::string & str, char c);
/// Returns true if the string str starts with beginning
bool StringStartsWith(const std::string & str, const std::string & beginning);
#endif #endif

View file

@ -1,5 +1,7 @@
#include <ArgParseCpp.hpp> #include <ArgParseCpp.hpp>
#define ASSERT_MSG(cond, msg); if(!(cond)) { std::cerr << msg << "\n"; exit(1); }
std::vector<std::string> SplitString(const std::string & str, char sep) std::vector<std::string> SplitString(const std::string & str, char sep)
{ {
std::vector<std::string> elems; std::vector<std::string> elems;
@ -60,7 +62,12 @@ ParserParam::ParserParam(const std::string & shortName_, const std::string & lon
longName(longName_), longName(longName_),
type(type_), type(type_),
valueType(valueType_), valueType(valueType_),
docString(docString_) docString(docString_),
validity(false),
boolVal(false),
intVal(0),
doubleVal(0.),
stringVal("")
{} {}
const std::string & ParserParam::GenerateDocString() const std::string & ParserParam::GenerateDocString()
@ -93,6 +100,56 @@ std::string ParserParam::GetHelpEntryStr() const
return res; return res;
} }
bool ParserParam::Parse(const std::string & arg, std::string argval)
{
if(!IsThisParamNamed(arg))
return false;
if(type == ArgParseCpp::StandaloneParam)
{
boolVal = true;
validity = true;
return true;
}
else if(type == ArgParseCpp::SingleValueParam)
{
if(!argval.size())
{
std::cerr << "Error : no value given for parameter \"" << SimpleName() << "\".\n";
return false;
}
}
else if(type == ArgParseCpp::EqualParam)
{
std::vector<std::string> args = SplitString(arg, '=');
if(args.size() != 2)
{
std::cerr << "Error : parameter \"" << SimpleName() << "\" must be written as \"" << shortName << "=value\" or \"" << longName << "=value\".\n";
return false;
}
argval = args[1];
}
// Actually parse the value
if(valueType == ArgParseCpp::Int)
intVal = atoi(argval.c_str());
else if(valueType == ArgParseCpp::Double)
doubleVal = atof(argval.c_str());
else if(valueType == ArgParseCpp::String)
stringVal = argval;
validity = true;
return true;
}
bool ParserParam::IsThisParamNamed(const std::string & name) const
{
return name == shortName || name == longName || StringStartsWith(name, shortName) || StringStartsWith(name, longName);
}
bool ParserParam::IsValid() const { return validity; }
// ------------------- ArgParseCpp::Parser ------------------- // ------------------- ArgParseCpp::Parser -------------------
Parser::Parser() {} Parser::Parser() {}
@ -103,17 +160,17 @@ void Parser::AddParameter(ParserParam param)
parserParams.push_back(param); parserParams.push_back(param);
} }
void Parser::Parse(int argc, char **argv) bool Parser::Parse(int argc, char **argv)
{ {
std::vector<std::string> args; std::vector<std::string> args;
for(int i = 0 ; i < argc ; i++) for(int i = 0 ; i < argc ; i++)
args.push_back(argv[i]); args.push_back(argv[i]);
Parse(args); return Parse(args);
} }
void Parser::Parse(const std::string & argString) { Parse(SplitString(argString, ' ')); } bool Parser::Parse(const std::string & argString) { return Parse(SplitString(argString, ' ')); }
void Parser::Parse(std::vector<std::string> args) bool Parser::Parse(std::vector<std::string> args)
{ {
CleanArgs(args); CleanArgs(args);
@ -121,57 +178,85 @@ void Parser::Parse(std::vector<std::string> args)
{ {
std::cerr << "Error : no arguments passed to the program.\n"; std::cerr << "Error : no arguments passed to the program.\n";
PrintHelp(); PrintHelp();
return;// exit(1); return false;
} }
else else
{ {
// DEBUG for(unsigned int iArg = 0 ; iArg < args.size() ; iArg++) // loop over all arguments
PRINT_VAR(args.size());
PRINT_VEC(args);
// Prepare all the boolean arguments
for(ParserParam const& param : parserParams) // loop over all parameters
if(param.type == ArgParseCpp::StandaloneParam) // if the parameter is a boolean type
boolParameters[param.SimpleName()] = false;
for(std::string const& arg : args)
{ {
std::string arg = args[iArg];
if(arg == "-h" || arg == "--help") if(arg == "-h" || arg == "--help")
{ {
PrintHelp(); PrintHelp();
return;// exit(1); return false;
} }
for(unsigned int iParam = 0 ; iParam < parserParams.size() ; iParam++) // loop over all parameters bool argumentParsed = false;
for(ParserParam & param : parserParams) // loop over all parameters
{ {
ParserParam const& param = parserParams[iParam]; if(iArg+1 < args.size())
PRINT_VAR(arg);
PRINT_VAR(param.shortName);
PRINT_VAR(param.longName);
std::cout << "----\n";
if(arg == param.shortName || arg == param.longName) // if the argument corresponds to this parameter
{ {
if(param.type == ArgParseCpp::StandaloneParam) bool argumentParsedWithThisParam = param.Parse(arg, args[iArg+1]);
boolParameters[param.SimpleName()] = true; argumentParsed |= argumentParsedWithThisParam;
else if(param.type == ArgParseCpp::SingleValueParam) if(argumentParsedWithThisParam && param.type == ArgParseCpp::SingleValueParam)
{ iArg++; // skip next argument, as it has already been
if(iParam+1 >= parserParams.size())
{
std::cerr << "Error : parameter " << arg << " must be followed by a single value.";
return;
} }
else
argumentParsed |= param.Parse(arg);
if(argumentParsed)
break;
}
}
}
return true;
}
if(param.valueType == ArgParseCpp::Int) bool Parser::GetArgumentValueBool(const std::string & argName)
intParameters[param.SimpleName()] = atoi(parserParams[++iParam]);// read next parameter as the value of this argument, and skip the next iteration, as the next argument has already been processed {
if(param.valueType == ArgParseCpp::Double) for(ParserParam const& param : parserParams)
doubleParameters[param.SimpleName()] = strtod(parserParams[++iParam]);// read next parameter as the value of this argument, and skip the next iteration, as the next argument has already been processed if(param.IsThisParamNamed(argName) || argName == param.SimpleName())
if(param.valueType == ArgParseCpp::String) {
stringParameters[param.SimpleName()] = parserParams[++iParam];// read next parameter as the value of this argument, and skip the next iteration, as the next argument has already been processed ASSERT_MSG(param.valueType == ArgParseCpp::Bool, "Error : parameter \"" << param.SimpleName() << "\" is not of type bool.");
} return param.boolVal;
} }
std::cerr << "Error : parameter \"" << argName << "\" is not defined.";
exit(1);
}
int Parser::GetArgumentValueInt(const std::string & argName)
{
for(ParserParam const& param : parserParams)
if(param.IsThisParamNamed(argName) || argName == param.SimpleName())
{
ASSERT_MSG(param.valueType == ArgParseCpp::Int, "Error : parameter \"" << param.SimpleName() << "\" is not of type int.");
return param.intVal;
} }
std::cerr << "Error : parameter \"" << argName << "\" is not defined.";
exit(1);
}
double Parser::GetArgumentValueDouble(const std::string & argName)
{
for(ParserParam const& param : parserParams)
if(param.IsThisParamNamed(argName) || argName == param.SimpleName())
{
ASSERT_MSG(param.valueType == ArgParseCpp::Double, "Error : parameter \"" << param.SimpleName() << "\" is not of type double.");
return param.doubleVal;
} }
std::cerr << "Error : parameter \"" << argName << "\" is not defined.";
exit(1);
}
std::string const& Parser::GetArgumentValueString(const std::string & argName)
{
for(ParserParam const& param : parserParams)
if(param.IsThisParamNamed(argName) || argName == param.SimpleName())
{
ASSERT_MSG(param.valueType == ArgParseCpp::String, "Error : parameter \"" << param.SimpleName() << "\" is not of type string.");
return param.stringVal;
} }
std::cerr << "Error : parameter \"" << argName << "\" is not defined.";
exit(1);
} }
void Parser::PrintHelp() const void Parser::PrintHelp() const
@ -191,18 +276,17 @@ void Parser::PrintHelp() const
void Parser::PrintParsedArguments() const void Parser::PrintParsedArguments() const
{ {
std::cout << "\nBoolean parameters\n------------------\n"; for(ParserParam const& arg : parserParams)
for(auto const& arg : boolParameters) {
std::cout << arg.first << "\t= " << arg.second << "\n"; if(arg.valueType == ArgParseCpp::Bool)
std::cout << "\nInteger parameters\n------------------\n"; std::cout << arg.SimpleName() << "\t= " << arg.boolVal << "\n";
for(auto const& arg : intParameters) if(arg.valueType == ArgParseCpp::Int)
std::cout << arg.first << "\t= " << arg.second << "\n"; std::cout << arg.SimpleName() << "\t= " << arg.intVal << "\n";
std::cout << "\nFloating-point parameters\n-------------------------\n"; if(arg.valueType == ArgParseCpp::Double)
for(auto const& arg : doubleParameters) std::cout << arg.SimpleName() << "\t= " << arg.doubleVal << "\n";
std::cout << arg.first << "\t= " << arg.second << "\n"; if(arg.valueType == ArgParseCpp::String)
std::cout << "\nString parameters\n------------------\n"; std::cout << arg.SimpleName() << "\t= " << arg.stringVal << "\n";
for(auto const& arg : stringParameters) }
std::cout << arg.first << "\t= " << arg.second << "\n";
} }
void Parser::CleanArgs(std::vector<std::string> & args) const void Parser::CleanArgs(std::vector<std::string> & args) const

View file

@ -39,12 +39,14 @@ int main(int argc, char *argv[])
if(0) if(0)
{// sanitize the input arguments and display help whenever -h or --help is seen {// sanitize the input arguments and display help whenever -h or --help is seen
std::string argString = "programName -h blabla hehe hoho lalaland"; std::string argString = "programName -h blabla hehe hoho lalaland";
PRINT_VAR(argString);
Parser parser; Parser parser;
parser.Parse(argString); parser.Parse(argString);
} }
if(0) if(0)
{// parse some boolean arguments {// parse some boolean arguments
std::string argString = "programName -v --all"; std::string argString = "programName -v --all";
PRINT_VAR(argString);
Parser parser; Parser parser;
parser.AddParameter(ArgParseCpp::ParserParam("-v", "--verbose", ArgParseCpp::StandaloneParam, ArgParseCpp::Bool, "Makes the program more verbose.")); parser.AddParameter(ArgParseCpp::ParserParam("-v", "--verbose", ArgParseCpp::StandaloneParam, ArgParseCpp::Bool, "Makes the program more verbose."));
parser.AddParameter(ArgParseCpp::ParserParam("-a", "--all", ArgParseCpp::StandaloneParam)); parser.AddParameter(ArgParseCpp::ParserParam("-a", "--all", ArgParseCpp::StandaloneParam));
@ -54,9 +56,10 @@ int main(int argc, char *argv[])
parser.Parse(argString); parser.Parse(argString);
parser.PrintParsedArguments(); parser.PrintParsedArguments();
} }
// if(0) if(0)
{// parse single value arguments {// parse single value arguments
std::string argString = "programName -a 5 -b 12.654 -s blabla_mon_negro hehe"; std::string argString = "programName -a 5 -b 12.654 -s blabla_mon_negro hehe";
PRINT_VAR(argString);
Parser parser; Parser parser;
parser.AddParameter(ArgParseCpp::ParserParam("-a", "--all", ArgParseCpp::SingleValueParam, ArgParseCpp::Int)); parser.AddParameter(ArgParseCpp::ParserParam("-a", "--all", ArgParseCpp::SingleValueParam, ArgParseCpp::Int));
parser.AddParameter(ArgParseCpp::ParserParam("-b", "--brief", ArgParseCpp::SingleValueParam, ArgParseCpp::Double)); parser.AddParameter(ArgParseCpp::ParserParam("-b", "--brief", ArgParseCpp::SingleValueParam, ArgParseCpp::Double));
@ -64,6 +67,48 @@ int main(int argc, char *argv[])
parser.PrintHelp(); parser.PrintHelp();
parser.Parse(argString); parser.Parse(argString);
parser.PrintParsedArguments(); parser.PrintParsedArguments();
PRINT_VAR(parser.GetArgumentValueInt("-a"));
PRINT_VAR(parser.GetArgumentValueInt("--all"));
PRINT_VAR(parser.GetArgumentValueDouble("brief"));
PRINT_VAR(parser.GetArgumentValueString("s"));
// PRINT_VAR(parser.GetArgumentValueInt("brief"));// wrong type = exit program
}
if(0)
{// parse equal-type arguments
std::string argString = "programName -a=5 -b=12.654 -s=blabla_mon_negro sfdg";
PRINT_VAR(argString);
Parser parser;
parser.AddParameter(ArgParseCpp::ParserParam("-a", "--all", ArgParseCpp::EqualParam, ArgParseCpp::Int));
parser.AddParameter(ArgParseCpp::ParserParam("-b", "--brief", ArgParseCpp::EqualParam, ArgParseCpp::Double));
parser.AddParameter(ArgParseCpp::ParserParam("-s", "", ArgParseCpp::EqualParam, ArgParseCpp::String));
parser.PrintHelp();
parser.Parse(argString);
parser.PrintParsedArguments();
PRINT_VAR(parser.GetArgumentValueInt("-a"));
PRINT_VAR(parser.GetArgumentValueInt("--all"));
PRINT_VAR(parser.GetArgumentValueDouble("brief"));
PRINT_VAR(parser.GetArgumentValueString("s"));
}
// if(0)
{// parse all types of arguments
std::string argString = "programName -v -a 5 -b=12.654 -s blabla_mon_negro sfdg";
PRINT_VAR(argString);
Parser parser;
parser.AddParameter(ArgParseCpp::ParserParam("-v", "--verbose", ArgParseCpp::StandaloneParam));
parser.AddParameter(ArgParseCpp::ParserParam("-i", "", ArgParseCpp::StandaloneParam));
parser.AddParameter(ArgParseCpp::ParserParam("-a", "--all", ArgParseCpp::SingleValueParam, ArgParseCpp::Int));
parser.AddParameter(ArgParseCpp::ParserParam("-b", "--brief", ArgParseCpp::EqualParam, ArgParseCpp::Double));
parser.AddParameter(ArgParseCpp::ParserParam("-s", "", ArgParseCpp::SingleValueParam, ArgParseCpp::String));
parser.PrintHelp();
parser.Parse(argString);
parser.PrintParsedArguments();
PRINT_VAR(parser.GetArgumentValueInt("-a"));
PRINT_VAR(parser.GetArgumentValueInt("--all"));
PRINT_VAR(parser.GetArgumentValueDouble("brief"));
PRINT_VAR(parser.GetArgumentValueString("s"));
PRINT_VAR(parser.GetArgumentValueBool("verbose"));
PRINT_VAR(parser.GetArgumentValueBool("i"));
} }
} }

View file

@ -1 +1,20 @@
#include <utils.hpp> #include <utils.hpp>
bool StringContains(const std::string & str, char c)
{
for(unsigned int i = 0 ; i < str.size() ; i++)
if(str[i] == c)
return true;
return false;
}
bool StringStartsWith(const std::string & str, const std::string & beginning)
{
if(str.size() < beginning.size() || !str.size() || !beginning.size())
return false;
for(unsigned int i = 0 ; i < beginning.size() ; i++)
if(beginning[i] != str[i])
return false;
return true;
}