Skip to content

Commit

Permalink
Merge pull request #331 from rouault/store_into
Browse files Browse the repository at this point in the history
Add Argument::store_into() functions
  • Loading branch information
p-ranav authored Mar 12, 2024
2 parents ce2d431 + 8784cc8 commit 874b939
Show file tree
Hide file tree
Showing 4 changed files with 247 additions and 1 deletion.
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
* [Joining values of repeated optional arguments](#joining-values-of-repeated-optional-arguments)
* [Repeating an argument to increase a value](#repeating-an-argument-to-increase-a-value)
* [Mutually Exclusive Group](#mutually-exclusive-group)
* [Storing values into variables](#store-into)
* [Negative Numbers](#negative-numbers)
* [Combining Positional and Optional Arguments](#combining-positional-and-optional-arguments)
* [Printing Help](#printing-help)
Expand Down Expand Up @@ -316,6 +317,36 @@ foo@bar:/home/dev/$ ./main
One of the arguments '--first VAR' or '--second VAR' is required
```

### Storing values into variables

It is possible to bind arguments to a variable storing their value, as an
alternative to explicitly calling ``program.get<T>(arg_name)`` or ``program[arg_name]``

This is currently implementeted for variables of type ``bool`` (this also
implicitly calls ``flag()``), ``int``, ``double``, ``std::string`` and
``std::vector<std::string>``. If the argument is not specified in the command
line, the default value (if set) is set into the variable.

```cpp
bool flagvar = false;
program.add_argument("--flagvar").store_into(flagvar);

int intvar = 0;
program.add_argument("--intvar").store_into(intvar);

double doublevar = 0;
program.add_argument("--doublevar").store_into(doublevar);

std::string strvar;
program.add_argument("--strvar").store_into(strvar);

std::vector<std::string> strvar_repeated;
program.add_argument("--strvar-repeated").append().store_into(strvar_repeated);

std::vector<std::string> strvar_multi_valued;
program.add_argument("--strvar-multi-valued").nargs(2).store_into(strvar_multi_valued);
```

### Negative Numbers

Optional arguments start with ```-```. Can ```argparse``` handle negative numbers? The answer is yes!
Expand Down
55 changes: 54 additions & 1 deletion include/argparse/argparse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,57 @@ class Argument {
return *this;
}

auto &store_into(bool &var) {
flag();
if (m_default_value.has_value()) {
var = std::any_cast<bool>(m_default_value);
}
action([&var](const auto & /*unused*/) { var = true; });
return *this;
}

auto &store_into(int &var) {
if (m_default_value.has_value()) {
var = std::any_cast<int>(m_default_value);
}
action([&var](const auto &s) {
var = details::parse_number<int, details::radix_10>()(s);
});
return *this;
}

auto &store_into(double &var) {
if (m_default_value.has_value()) {
var = std::any_cast<double>(m_default_value);
}
action([&var](const auto &s) {
var = details::parse_number<double, details::chars_format::general>()(s);
});
return *this;
}

auto &store_into(std::string &var) {
if (m_default_value.has_value()) {
var = std::any_cast<std::string>(m_default_value);
}
action([&var](const std::string &s) { var = s; });
return *this;
}

auto &store_into(std::vector<std::string> &var) {
if (m_default_value.has_value()) {
var = std::any_cast<std::vector<std::string>>(m_default_value);
}
action([this, &var](const std::string &s) {
if (!m_is_used) {
var.clear();
}
m_is_used = true;
var.push_back(s);
});
return *this;
}

auto &append() {
m_is_repeatable = true;
return *this;
Expand Down Expand Up @@ -852,7 +903,6 @@ class Argument {
if (!m_is_repeatable && m_is_used) {
throw std::runtime_error("Duplicate argument");
}
m_is_used = true;
m_used_name = used_name;

if (m_choices.has_value()) {
Expand All @@ -875,6 +925,7 @@ class Argument {
if (num_args_max == 0) {
m_values.emplace_back(m_implicit_value);
std::visit([](const auto &f) { f({}); }, m_action);
m_is_used = true;
return start;
}
if ((dist = static_cast<std::size_t>(std::distance(start, end))) >=
Expand Down Expand Up @@ -912,9 +963,11 @@ class Argument {
Argument &self;
};
std::visit(ActionApply{start, end, *this}, m_action);
m_is_used = true;
return end;
}
if (m_default_value.has_value()) {
m_is_used = true;
return start;
}
throw std::runtime_error("Too few arguments for '" +
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ file(GLOB ARGPARSE_TEST_SOURCES
test_repr.cpp
test_required_arguments.cpp
test_scan.cpp
test_store_into.cpp
test_stringstream.cpp
test_version.cpp
test_subparsers.cpp
Expand Down
161 changes: 161 additions & 0 deletions test/test_store_into.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
#ifdef WITH_MODULE
import argparse;
#else
#include <argparse/argparse.hpp>
#endif
#include <doctest.hpp>

using doctest::test_suite;

TEST_CASE("Test store_into(bool), flag not specified" *
test_suite("store_into")) {
argparse::ArgumentParser program("test");
bool flag;
program.add_argument("--flag").store_into(flag);

program.parse_args({"./test.exe"});
REQUIRE(flag == false);
}

TEST_CASE("Test store_into(bool), flag specified" *
test_suite("store_into")) {
argparse::ArgumentParser program("test");
bool flag;
program.add_argument("--flag").store_into(flag);

program.parse_args({"./test.exe", "--flag"});
REQUIRE(flag == true);
}

TEST_CASE("Test store_into(int), no default value, non specified" *
test_suite("store_into")) {
argparse::ArgumentParser program("test");
int res = -1;
program.add_argument("--int-opt").store_into(res);

program.parse_args({"./test.exe"});
REQUIRE(res == -1);
}

TEST_CASE("Test store_into(int), default value, non specified" *
test_suite("store_into")) {
argparse::ArgumentParser program("test");
int res = -1;
program.add_argument("--int-opt").default_value(3).store_into(res);

program.parse_args({"./test.exe"});
REQUIRE(res == 3);
}

TEST_CASE("Test store_into(int), default value, specified" *
test_suite("store_into")) {
argparse::ArgumentParser program("test");
int res = -1;
program.add_argument("--int-opt").default_value(3).store_into(res);

program.parse_args({"./test.exe", "--int-opt", "5"});
REQUIRE(res == 5);
}

TEST_CASE("Test store_into(double), no default value, non specified" *
test_suite("store_into")) {
argparse::ArgumentParser program("test");
double res = -1;
program.add_argument("--double-opt").store_into(res);

program.parse_args({"./test.exe"});
REQUIRE(res == -1);
}

TEST_CASE("Test store_into(double), default value, non specified" *
test_suite("store_into")) {
argparse::ArgumentParser program("test");
double res = -1;
program.add_argument("--double-opt").default_value(3.5).store_into(res);

program.parse_args({"./test.exe"});
REQUIRE(res == 3.5);
}

TEST_CASE("Test store_into(double), default value, specified" *
test_suite("store_into")) {
argparse::ArgumentParser program("test");
double res = -1;
program.add_argument("--double-opt").default_value(3.5).store_into(res);

program.parse_args({"./test.exe", "--double-opt", "5.5"});
REQUIRE(res == 5.5);
}

TEST_CASE("Test store_into(string), no default value, non specified" *
test_suite("store_into")) {
argparse::ArgumentParser program("test");
std::string res = "init";
program.add_argument("--str-opt").store_into(res);

program.parse_args({"./test.exe"});
REQUIRE(res == "init");
}

TEST_CASE("Test store_into(string), default value, non specified" *
test_suite("store_into")) {
argparse::ArgumentParser program("test");
std::string res;
program.add_argument("--str-opt").default_value("default").store_into(res);

program.parse_args({"./test.exe"});
REQUIRE(res == "default");
}

TEST_CASE("Test store_into(string), default value, specified" *
test_suite("store_into")) {
argparse::ArgumentParser program("test");
std::string res;
program.add_argument("--str-opt").default_value("default").store_into(res);

program.parse_args({"./test.exe", "--str-opt", "foo"});
REQUIRE(res == "foo");
}

TEST_CASE("Test store_into(vector of string), no default value, non specified" *
test_suite("store_into")) {
argparse::ArgumentParser program("test");
std::vector<std::string> res;
program.add_argument("--strvector-opt").append().store_into(res);

program.parse_args({"./test.exe"});
REQUIRE(res == std::vector<std::string>{});
}

TEST_CASE("Test store_into(vector of string), default value, non specified" *
test_suite("store_into")) {
argparse::ArgumentParser program("test");
std::vector<std::string> res;
program.add_argument("--strvector-opt").append().default_value(
std::vector<std::string>{"a", "b"}).store_into(res);

program.parse_args({"./test.exe"});
REQUIRE(res == std::vector<std::string>{"a", "b"});
}

TEST_CASE("Test store_into(vector of string), default value, specified" *
test_suite("store_into")) {
argparse::ArgumentParser program("test");
std::vector<std::string> res;
program.add_argument("--strvector-opt").append().default_value(
std::vector<std::string>{"a", "b"}).store_into(res);

program.parse_args({"./test.exe", "--strvector-opt", "foo", "--strvector-opt", "bar"});
REQUIRE(res == std::vector<std::string>{"foo", "bar"});
}

TEST_CASE("Test store_into(vector of string), default value, multi valued, specified" *
test_suite("store_into")) {
argparse::ArgumentParser program("test");
std::vector<std::string> res;
program.add_argument("--strvector-opt").nargs(2).default_value(
std::vector<std::string>{"a", "b"}).store_into(res);

program.parse_args({"./test.exe", "--strvector-opt", "foo", "bar"});
REQUIRE(res == std::vector<std::string>{"foo", "bar"});
}

0 comments on commit 874b939

Please sign in to comment.