Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Argument::store_into() functions #331

Merged
merged 1 commit into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -315,6 +316,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"});
}
Loading