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

This commit is contained in:
Pranav Srinivas Kumar 2019-03-31 21:25:50 -04:00
parent 6a4c92726b
commit 70c2fcfdf1
2 changed files with 127 additions and 103 deletions

View File

@ -275,108 +275,13 @@ class ArgumentParser {
} }
void parse_args(const std::vector<std::string>& aArguments) { void parse_args(const std::vector<std::string>& aArguments) {
std::vector<char*> argv; parse_args_internal(aArguments);
for (const auto& arg : aArguments) parse_args_validate();
argv.push_back((char*)arg.data());
argv.push_back(nullptr);
return parse_args(argv.size() - 1, argv.data());
} }
void parse_args(int argc, char * argv[]) { void parse_args(int argc, char * argv[]) {
if (mProgramName == "" && argc > 0) parse_args_internal(argc, argv);
mProgramName = argv[0]; parse_args_validate();
for (int i = 1; i < argc; i++) {
auto tCurrentArgument = std::string(argv[i]);
if (tCurrentArgument == "-h" || tCurrentArgument == "--help") {
print_help();
exit(0);
}
std::map<std::string, std::shared_ptr<Argument>>::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<std::string, std::shared_ptr<Argument>>::iterator tIterator = mArgumentMap.find("-" + tArgument);
if (tIterator != mArgumentMap.end()) {
auto tArgumentObject = tIterator->second;
tNumArgs = tArgumentObject->mNumArgs;
}
std::vector<std::string> 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<std::string, std::shared_ptr<Argument>>::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;
}
}
}
} }
template <typename T = std::string> template <typename T = std::string>
@ -508,6 +413,125 @@ class ArgumentParser {
return (tIterator != mArgumentMap.end()); return (tIterator != mArgumentMap.end());
} }
void parse_args_internal(const std::vector<std::string>& aArguments) {
std::vector<char*> 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<std::string, std::shared_ptr<Argument>>::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<std::string, std::shared_ptr<Argument>>::iterator tIterator = mArgumentMap.find("-" + tArgument);
if (tIterator != mArgumentMap.end()) {
auto tArgumentObject = tIterator->second;
tNumArgs = tArgumentObject->mNumArgs;
}
std::vector<std::string> 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<std::string, std::shared_ptr<Argument>>::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 get_length_of_longest_argument() {
size_t tResult = 0; size_t tResult = 0;
for (size_t i = 0; i < mPositionalArguments.size(); i++) { for (size_t i = 0; i < mPositionalArguments.size(); i++) {

View File

@ -30,20 +30,20 @@ TEST_CASE("Parse list of arguments", "[vector]") {
TEST_CASE("Parse list of arguments with default values", "[vector]") { TEST_CASE("Parse list of arguments with default values", "[vector]") {
argparse::ArgumentParser program("test"); argparse::ArgumentParser program("test");
program.add_argument("input") program.add_argument("--input")
.default_value(std::list<int>{1, 2, 3, 4, 5}) .default_value(std::list<int>{1, 2, 3, 4, 5})
.nargs(2); .nargs(5);
program.parse_args({ "test" }); program.parse_args({ "test" });
auto inputs = program.get<std::list<int>>("input"); auto inputs = program.get<std::list<int>>("--input");
REQUIRE(inputs.size() == 5); REQUIRE(inputs.size() == 5);
REQUIRE(argparse::get_from_list(inputs, 0) == 1); REQUIRE(argparse::get_from_list(inputs, 0) == 1);
REQUIRE(argparse::get_from_list(inputs, 1) == 2); REQUIRE(argparse::get_from_list(inputs, 1) == 2);
REQUIRE(argparse::get_from_list(inputs, 2) == 3); REQUIRE(argparse::get_from_list(inputs, 2) == 3);
REQUIRE(argparse::get_from_list(inputs, 3) == 4); REQUIRE(argparse::get_from_list(inputs, 3) == 4);
REQUIRE(argparse::get_from_list(inputs, 4) == 5); REQUIRE(argparse::get_from_list(inputs, 4) == 5);
REQUIRE(program["input"] == std::list<int>{1, 2, 3, 4, 5}); REQUIRE(program["--input"] == std::list<int>{1, 2, 3, 4, 5});
} }
TEST_CASE("Parse list of arguments and save in an object", "[vector]") { TEST_CASE("Parse list of arguments and save in an object", "[vector]") {