Added support for compound arguments, e.g., ./main -aux foo bar

This commit is contained in:
Pranav Srinivas Kumar 2019-03-31 10:27:33 -04:00
parent 41009046f2
commit 07cad7ac9e
2 changed files with 130 additions and 26 deletions

View File

@ -70,6 +70,7 @@ struct Argument {
Argument& implicit_value(std::any aImplicitValue) {
mImplicitValue = aImplicitValue;
mNumArgs = 0;
return *this;
}
@ -156,7 +157,7 @@ class ArgumentParser {
add_argument_internal(tArgument, Fargs...);
for (auto& mName : tArgument->mNames) {
if (starts_with(mName, "--") || starts_with(mName, "-"))
if (is_optional(mName))
tArgument->mIsOptional = true;
}
@ -189,8 +190,8 @@ class ArgumentParser {
// 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 left nargs to be default, i.e., 1 and provided an implicit value
if (tCount == 0 || (tArgument->mImplicitValue.has_value() && tCount == 1)) {
// (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("");
@ -213,32 +214,57 @@ class ArgumentParser {
}
}
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()) {
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]));
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);
}
tCount -= 1;
if (tCount > 0) i += 1;
}
if (tCount == 0)
mNextPositionalArgument += 1;
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()) {
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;
}
}
}
}
@ -279,6 +305,15 @@ class ArgumentParser {
return *aArgument;
}
bool is_optional(const std::string& aName) {
return (starts_with(aName, "--") || starts_with(aName, "-"));
}
bool is_valid_argument(const std::string& aName) {
std::map<std::string, std::shared_ptr<Argument>>::iterator tIterator = mArgumentMap.find(aName);
return (tIterator != mArgumentMap.end());
}
std::string mProgramName;
std::vector<std::shared_ptr<Argument>> mPositionalArguments;
size_t mNextPositionalArgument;

View File

@ -25,3 +25,72 @@ TEST_CASE("Parse toggle arguments with implicit value", "[parse_args]") {
REQUIRE(arguments.size() == 1);
REQUIRE(program.get<bool>("--verbose") == true);
}
TEST_CASE("Parse multiple toggle arguments with implicit values", "[parse_args]") {
argparse::ArgumentParser program("test");
program.add_argument("-a")
.default_value(false)
.implicit_value(true);
program.add_argument("-u")
.default_value(false)
.implicit_value(true);
program.add_argument("-x")
.default_value(false)
.implicit_value(true);
program.parse_args({ "./test.exe", "-a", "-x" });
auto arguments = program.get_arguments();
REQUIRE(arguments.size() == 3);
REQUIRE(program.get<bool>("-a") == true);
REQUIRE(program.get<bool>("-u") == false);
REQUIRE(program.get<bool>("-x") == true);
}
TEST_CASE("Parse compound toggle arguments with implicit values", "[parse_args]") {
argparse::ArgumentParser program("test");
program.add_argument("-a")
.default_value(false)
.implicit_value(true);
program.add_argument("-u")
.default_value(false)
.implicit_value(true);
program.add_argument("-x")
.default_value(false)
.implicit_value(true);
program.parse_args({ "./test.exe", "-aux" });
auto arguments = program.get_arguments();
REQUIRE(arguments.size() == 3);
REQUIRE(program.get<bool>("-a") == true);
REQUIRE(program.get<bool>("-u") == true);
REQUIRE(program.get<bool>("-x") == true);
}
TEST_CASE("Parse compound toggle arguments with implicit values and nargs", "[parse_args]") {
argparse::ArgumentParser program("test");
program.add_argument("-a")
.default_value(false)
.implicit_value(true);
program.add_argument("-b")
.default_value(false)
.implicit_value(true);
program.add_argument("-c")
.nargs(2)
.action([](const std::string& value) { return std::stof(value); });
program.parse_args({ "./test.exe", "-abc", "3.14", "2.718" });
auto arguments = program.get_arguments();
REQUIRE(arguments.size() == 3);
REQUIRE(program.get<bool>("-a") == true);
REQUIRE(program.get<bool>("-b") == true);
auto c = program.get<std::vector<float>>("-c");
REQUIRE(c.size() == 2);
REQUIRE(c[0] == 3.14f);
REQUIRE(c[1] == 2.718f);
}