diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bb41c74 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +build/* +argParseCpp +argParseTest.exe +*.bat diff --git a/include/ArgParseCpp.hpp b/include/ArgParseCpp.hpp index 9e49c3f..4cd2324 100644 --- a/include/ArgParseCpp.hpp +++ b/include/ArgParseCpp.hpp @@ -55,12 +55,29 @@ class ParserParam /// Returns the help entry string : "-p --param value Parameter "param" with value "value_param" of type integer number" 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: std::string shortName; std::string longName; ParameterType type; ParameterValueType valueType; 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. @@ -74,13 +91,13 @@ class Parser void AddParameter(ParserParam param); /// 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. - void Parse(const std::string & argString); + bool Parse(const std::string & argString); /// Parses the arguments passed as parameters. - void Parse(std::vector args); + bool Parse(std::vector args); /// Prints the custom help message if it has been defined, or an automatically constructed one. void PrintHelp() const; @@ -88,11 +105,11 @@ class Parser /// Prints the parsed arguments and their associated values. void PrintParsedArguments() const; - /// Returns the value of the specified argument. A conversion is done between the input text and the output value type. - template T GetArgumentValue(const std::string & argName) - { - - } + /// Returns the value of the specified argument. + 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: /// Cleans the argument list passed as parameter. @@ -100,12 +117,6 @@ class Parser protected: std::vector parserParams; - - std::map boolParameters; - std::map intParameters; - std::map doubleParameters; - std::map stringParameters; - std::string customHelpMessage; }; diff --git a/include/utils.hpp b/include/utils.hpp index ddb2967..e39fde2 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -7,4 +7,10 @@ #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"; } +/// 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 diff --git a/src/ArgParseCpp.cpp b/src/ArgParseCpp.cpp index ed8df08..2a23743 100644 --- a/src/ArgParseCpp.cpp +++ b/src/ArgParseCpp.cpp @@ -1,5 +1,7 @@ #include +#define ASSERT_MSG(cond, msg); if(!(cond)) { std::cerr << msg << "\n"; exit(1); } + std::vector SplitString(const std::string & str, char sep) { std::vector elems; @@ -60,7 +62,12 @@ ParserParam::ParserParam(const std::string & shortName_, const std::string & lon longName(longName_), type(type_), valueType(valueType_), - docString(docString_) + docString(docString_), + validity(false), + boolVal(false), + intVal(0), + doubleVal(0.), + stringVal("") {} const std::string & ParserParam::GenerateDocString() @@ -93,6 +100,56 @@ std::string ParserParam::GetHelpEntryStr() const 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 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 ------------------- Parser::Parser() {} @@ -103,17 +160,17 @@ void Parser::AddParameter(ParserParam param) parserParams.push_back(param); } -void Parser::Parse(int argc, char **argv) +bool Parser::Parse(int argc, char **argv) { std::vector args; for(int i = 0 ; i < argc ; 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 args) +bool Parser::Parse(std::vector args) { CleanArgs(args); @@ -121,57 +178,85 @@ void Parser::Parse(std::vector args) { std::cerr << "Error : no arguments passed to the program.\n"; PrintHelp(); - return;// exit(1); + return false; } else { - // DEBUG - 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) + for(unsigned int iArg = 0 ; iArg < args.size() ; iArg++) // loop over all arguments { + std::string arg = args[iArg]; if(arg == "-h" || arg == "--help") { 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]; - 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(iArg+1 < args.size()) { - if(param.type == ArgParseCpp::StandaloneParam) - boolParameters[param.SimpleName()] = true; - else if(param.type == ArgParseCpp::SingleValueParam) - { - if(iParam+1 >= parserParams.size()) - { - std::cerr << "Error : parameter " << arg << " must be followed by a single value."; - return; - } - - if(param.valueType == ArgParseCpp::Int) - 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) - 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.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 - } + bool argumentParsedWithThisParam = param.Parse(arg, args[iArg+1]); + argumentParsed |= argumentParsedWithThisParam; + if(argumentParsedWithThisParam && param.type == ArgParseCpp::SingleValueParam) + iArg++; // skip next argument, as it has already been } + else + argumentParsed |= param.Parse(arg); + if(argumentParsed) + break; } } } + return true; +} + +bool Parser::GetArgumentValueBool(const std::string & argName) +{ + for(ParserParam const& param : parserParams) + if(param.IsThisParamNamed(argName) || argName == param.SimpleName()) + { + 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 @@ -191,18 +276,17 @@ void Parser::PrintHelp() const void Parser::PrintParsedArguments() const { - std::cout << "\nBoolean parameters\n------------------\n"; - for(auto const& arg : boolParameters) - std::cout << arg.first << "\t= " << arg.second << "\n"; - std::cout << "\nInteger parameters\n------------------\n"; - for(auto const& arg : intParameters) - std::cout << arg.first << "\t= " << arg.second << "\n"; - std::cout << "\nFloating-point parameters\n-------------------------\n"; - for(auto const& arg : doubleParameters) - std::cout << arg.first << "\t= " << arg.second << "\n"; - std::cout << "\nString parameters\n------------------\n"; - for(auto const& arg : stringParameters) - std::cout << arg.first << "\t= " << arg.second << "\n"; + for(ParserParam const& arg : parserParams) + { + if(arg.valueType == ArgParseCpp::Bool) + std::cout << arg.SimpleName() << "\t= " << arg.boolVal << "\n"; + if(arg.valueType == ArgParseCpp::Int) + std::cout << arg.SimpleName() << "\t= " << arg.intVal << "\n"; + if(arg.valueType == ArgParseCpp::Double) + std::cout << arg.SimpleName() << "\t= " << arg.doubleVal << "\n"; + if(arg.valueType == ArgParseCpp::String) + std::cout << arg.SimpleName() << "\t= " << arg.stringVal << "\n"; + } } void Parser::CleanArgs(std::vector & args) const diff --git a/src/test_ArgParseCpp.cpp b/src/test_ArgParseCpp.cpp index 86f59f0..9023037 100644 --- a/src/test_ArgParseCpp.cpp +++ b/src/test_ArgParseCpp.cpp @@ -39,12 +39,14 @@ int main(int argc, char *argv[]) if(0) {// sanitize the input arguments and display help whenever -h or --help is seen std::string argString = "programName -h blabla hehe hoho lalaland"; + PRINT_VAR(argString); Parser parser; parser.Parse(argString); } if(0) {// parse some boolean arguments std::string argString = "programName -v --all"; + PRINT_VAR(argString); Parser parser; parser.AddParameter(ArgParseCpp::ParserParam("-v", "--verbose", ArgParseCpp::StandaloneParam, ArgParseCpp::Bool, "Makes the program more verbose.")); parser.AddParameter(ArgParseCpp::ParserParam("-a", "--all", ArgParseCpp::StandaloneParam)); @@ -54,9 +56,10 @@ int main(int argc, char *argv[]) parser.Parse(argString); parser.PrintParsedArguments(); } - // if(0) + if(0) {// parse single value arguments std::string argString = "programName -a 5 -b 12.654 -s blabla_mon_negro hehe"; + PRINT_VAR(argString); Parser parser; parser.AddParameter(ArgParseCpp::ParserParam("-a", "--all", ArgParseCpp::SingleValueParam, ArgParseCpp::Int)); parser.AddParameter(ArgParseCpp::ParserParam("-b", "--brief", ArgParseCpp::SingleValueParam, ArgParseCpp::Double)); @@ -64,6 +67,48 @@ int main(int argc, char *argv[]) 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.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")); } } diff --git a/src/utils.cpp b/src/utils.cpp index efcec79..456fa3e 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -1 +1,20 @@ #include + +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; +}