From 70c2fcfdf153a7d620882c0829eb13fa4b9f6db3 Mon Sep 17 00:00:00 2001 From: Pranav Srinivas Kumar Date: Sun, 31 Mar 2019 21:25:50 -0400 Subject: [PATCH] First bit of error checking - Cleaned up parse_args methods - Added parse_args_validate() method that checks if all positional arguments have been provided or not and prints help --- src/argparse.hpp | 222 ++++++++++++++++------------- tests/test_container_arguments.hpp | 8 +- 2 files changed, 127 insertions(+), 103 deletions(-) diff --git a/src/argparse.hpp b/src/argparse.hpp index f4316c2..b25847e 100644 --- a/src/argparse.hpp +++ b/src/argparse.hpp @@ -275,108 +275,13 @@ class ArgumentParser { } void parse_args(const std::vector& aArguments) { - std::vector argv; - for (const auto& arg : aArguments) - argv.push_back((char*)arg.data()); - argv.push_back(nullptr); - return parse_args(argv.size() - 1, argv.data()); + parse_args_internal(aArguments); + parse_args_validate(); } void parse_args(int argc, char * argv[]) { - if (mProgramName == "" && argc > 0) - mProgramName = argv[0]; - for (int i = 1; i < argc; i++) { - auto tCurrentArgument = std::string(argv[i]); - if (tCurrentArgument == "-h" || tCurrentArgument == "--help") { - print_help(); - exit(0); - } - std::map>::iterator tIterator = mArgumentMap.find(argv[i]); - if (tIterator != mArgumentMap.end()) { - // Start parsing optional argument - auto tArgument = tIterator->second; - auto tCount = tArgument->mNumArgs; - - // Check to see if implicit value should be used - // Two cases to handle here: - // (1) User has explicitly programmed nargs to be 0 - // (2) User has provided an implicit value, which also sets nargs to 0 - if (tCount == 0) { - // Use implicit value for this optional argument - tArgument->mValues.push_back(tArgument->mImplicitValue); - tArgument->mRawValues.push_back(""); - tCount = 0; - } - while (tCount > 0) { - i = i + 1; - if (i < argc) { - tArgument->mRawValues.push_back(argv[i]); - if (tArgument->mAction != nullptr) - tArgument->mValues.push_back(tArgument->mAction(argv[i])); - else { - if (tArgument->mDefaultValue.has_value()) - tArgument->mValues.push_back(tArgument->mDefaultValue); - else - tArgument->mValues.push_back(std::string(argv[i])); - } - } - tCount -= 1; - } - } - else { - if (is_optional(argv[i])) { - // This is possibly a compound optional argument - // Example: We have three optional arguments -a, -u and -x - // The user provides ./main -aux ... - // Here -aux is a compound optional argument - std::string tCompoundArgument = std::string(argv[i]); - for (size_t j = 1; j < tCompoundArgument.size(); j++) { - std::string tArgument(1, tCompoundArgument[j]); - size_t tNumArgs = 0; - std::map>::iterator tIterator = mArgumentMap.find("-" + tArgument); - if (tIterator != mArgumentMap.end()) { - auto tArgumentObject = tIterator->second; - tNumArgs = tArgumentObject->mNumArgs; - } - std::vector tArgumentsForRecursiveParsing = { "", "-" + tArgument }; - while (tNumArgs > 0) { - i += 1; - tArgumentsForRecursiveParsing.push_back(argv[i]); - tNumArgs -= 1; - } - parse_args(tArgumentsForRecursiveParsing); - } - } - else { - // This is a positional argument. - // Parse and save into mPositionalArguments vector - auto tArgument = mPositionalArguments[mNextPositionalArgument]; - auto tCount = tArgument->mNumArgs - tArgument->mRawValues.size(); - while (tCount > 0) { - std::map>::iterator tIterator = mArgumentMap.find(argv[i]); - if (tIterator != mArgumentMap.end() || is_optional(argv[i])) { - i = i - 1; - break; - } - if (i < argc) { - tArgument->mRawValues.push_back(argv[i]); - if (tArgument->mAction != nullptr) - tArgument->mValues.push_back(tArgument->mAction(argv[i])); - else { - if (tArgument->mDefaultValue.has_value()) - tArgument->mValues.push_back(tArgument->mDefaultValue); - else - tArgument->mValues.push_back(std::string(argv[i])); - } - } - tCount -= 1; - if (tCount > 0) i += 1; - } - if (tCount == 0) - mNextPositionalArgument += 1; - } - } - } + parse_args_internal(argc, argv); + parse_args_validate(); } template @@ -508,6 +413,125 @@ class ArgumentParser { return (tIterator != mArgumentMap.end()); } + void parse_args_internal(const std::vector& aArguments) { + std::vector argv; + for (const auto& arg : aArguments) + argv.push_back((char*)arg.data()); + argv.push_back(nullptr); + return parse_args_internal(argv.size() - 1, argv.data()); + } + + void parse_args_internal(int argc, char * argv[]) { + if (mProgramName == "" && argc > 0) + mProgramName = argv[0]; + for (int i = 1; i < argc; i++) { + auto tCurrentArgument = std::string(argv[i]); + if (tCurrentArgument == "-h" || tCurrentArgument == "--help") { + print_help(); + exit(0); + } + std::map>::iterator tIterator = mArgumentMap.find(argv[i]); + if (tIterator != mArgumentMap.end()) { + // Start parsing optional argument + auto tArgument = tIterator->second; + auto tCount = tArgument->mNumArgs; + + // Check to see if implicit value should be used + // Two cases to handle here: + // (1) User has explicitly programmed nargs to be 0 + // (2) User has provided an implicit value, which also sets nargs to 0 + if (tCount == 0) { + // Use implicit value for this optional argument + tArgument->mValues.push_back(tArgument->mImplicitValue); + tArgument->mRawValues.push_back(""); + tCount = 0; + } + while (tCount > 0) { + i = i + 1; + if (i < argc) { + tArgument->mRawValues.push_back(argv[i]); + if (tArgument->mAction != nullptr) + tArgument->mValues.push_back(tArgument->mAction(argv[i])); + else { + if (tArgument->mDefaultValue.has_value()) + tArgument->mValues.push_back(tArgument->mDefaultValue); + else + tArgument->mValues.push_back(std::string(argv[i])); + } + } + tCount -= 1; + } + } + else { + if (is_optional(argv[i])) { + // This is possibly a compound optional argument + // Example: We have three optional arguments -a, -u and -x + // The user provides ./main -aux ... + // Here -aux is a compound optional argument + std::string tCompoundArgument = std::string(argv[i]); + for (size_t j = 1; j < tCompoundArgument.size(); j++) { + std::string tArgument(1, tCompoundArgument[j]); + size_t tNumArgs = 0; + std::map>::iterator tIterator = mArgumentMap.find("-" + tArgument); + if (tIterator != mArgumentMap.end()) { + auto tArgumentObject = tIterator->second; + tNumArgs = tArgumentObject->mNumArgs; + } + std::vector tArgumentsForRecursiveParsing = { "", "-" + tArgument }; + while (tNumArgs > 0) { + i += 1; + tArgumentsForRecursiveParsing.push_back(argv[i]); + tNumArgs -= 1; + } + parse_args_internal(tArgumentsForRecursiveParsing); + } + } + else { + // This is a positional argument. + // Parse and save into mPositionalArguments vector + auto tArgument = mPositionalArguments[mNextPositionalArgument]; + auto tCount = tArgument->mNumArgs - tArgument->mRawValues.size(); + while (tCount > 0) { + std::map>::iterator tIterator = mArgumentMap.find(argv[i]); + if (tIterator != mArgumentMap.end() || is_optional(argv[i])) { + i = i - 1; + break; + } + if (i < argc) { + tArgument->mRawValues.push_back(argv[i]); + if (tArgument->mAction != nullptr) + tArgument->mValues.push_back(tArgument->mAction(argv[i])); + else { + if (tArgument->mDefaultValue.has_value()) + tArgument->mValues.push_back(tArgument->mDefaultValue); + else + tArgument->mValues.push_back(std::string(argv[i])); + } + } + tCount -= 1; + if (tCount > 0) i += 1; + } + if (tCount == 0) + mNextPositionalArgument += 1; + } + } + } + } + + void parse_args_validate() { + // Check if all positional arguments are parsed + for (size_t i = 0; i < mPositionalArguments.size(); i++) { + auto tArgument = mPositionalArguments[i]; + if (tArgument->mValues.size() != tArgument->mNumArgs) { + std::cout << "error: " << tArgument->mNames[0] << ": expected " + << tArgument->mNumArgs << " arguments. " + << tArgument->mValues.size() << " provided.\n" << std::endl; + print_help(); + exit(0); + } + } + } + size_t get_length_of_longest_argument() { size_t tResult = 0; for (size_t i = 0; i < mPositionalArguments.size(); i++) { diff --git a/tests/test_container_arguments.hpp b/tests/test_container_arguments.hpp index 6e34eaf..7b12e16 100644 --- a/tests/test_container_arguments.hpp +++ b/tests/test_container_arguments.hpp @@ -30,20 +30,20 @@ TEST_CASE("Parse list of arguments", "[vector]") { TEST_CASE("Parse list of arguments with default values", "[vector]") { argparse::ArgumentParser program("test"); - program.add_argument("input") + program.add_argument("--input") .default_value(std::list{1, 2, 3, 4, 5}) - .nargs(2); + .nargs(5); program.parse_args({ "test" }); - auto inputs = program.get>("input"); + auto inputs = program.get>("--input"); REQUIRE(inputs.size() == 5); REQUIRE(argparse::get_from_list(inputs, 0) == 1); REQUIRE(argparse::get_from_list(inputs, 1) == 2); REQUIRE(argparse::get_from_list(inputs, 2) == 3); REQUIRE(argparse::get_from_list(inputs, 3) == 4); REQUIRE(argparse::get_from_list(inputs, 4) == 5); - REQUIRE(program["input"] == std::list{1, 2, 3, 4, 5}); + REQUIRE(program["--input"] == std::list{1, 2, 3, 4, 5}); } TEST_CASE("Parse list of arguments and save in an object", "[vector]") {