Skip to content

Commit

Permalink
Merge pull request #356 from elpaso/multiple-actions
Browse files Browse the repository at this point in the history
FEATURE: multiple actions
  • Loading branch information
p-ranav authored Jan 20, 2025
2 parents e39aa89 + 8dead89 commit 22b54b8
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 7 deletions.
25 changes: 18 additions & 7 deletions include/argparse/argparse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -679,9 +679,9 @@ class Argument {
std::is_void_v<std::invoke_result_t<F, Args..., std::string const>>,
void_action, valued_action>;
if constexpr (sizeof...(Args) == 0) {
m_action.emplace<action_type>(std::forward<F>(callable));
m_actions.emplace_back<action_type>(std::forward<F>(callable));
} else {
m_action.emplace<action_type>(
m_actions.emplace_back<action_type>(
[f = std::forward<F>(callable),
tup = std::make_tuple(std::forward<Args>(bound_args)...)](
std::string const &opt) mutable {
Expand Down Expand Up @@ -1014,7 +1014,12 @@ class Argument {
if (num_args_max == 0) {
if (!dry_run) {
m_values.emplace_back(m_implicit_value);
std::visit([](const auto &f) { f({}); }, m_action);
for(auto &action: m_actions) {
std::visit([&](const auto &f) { f({}); }, action);
}
if(m_actions.empty()){
std::visit([&](const auto &f) { f({}); }, m_default_action);
}
m_is_used = true;
}
return start;
Expand Down Expand Up @@ -1054,7 +1059,12 @@ class Argument {
Argument &self;
};
if (!dry_run) {
std::visit(ActionApply{start, end, *this}, m_action);
for(auto &action: m_actions) {
std::visit(ActionApply{start, end, *this}, action);
}
if(m_actions.empty()){
std::visit(ActionApply{start, end, *this}, m_default_action);
}
m_is_used = true;
}
return end;
Expand Down Expand Up @@ -1604,9 +1614,10 @@ class Argument {
std::optional<std::vector<std::string>> m_choices{std::nullopt};
using valued_action = std::function<std::any(const std::string &)>;
using void_action = std::function<void(const std::string &)>;
std::variant<valued_action, void_action> m_action{
std::in_place_type<valued_action>,
[](const std::string &value) { return value; }};
std::vector<std::variant<valued_action, void_action>> m_actions;
std::variant<valued_action, void_action> m_default_action{
std::in_place_type<valued_action>,
[](const std::string &value) { return value; }};
std::vector<std::any> m_values;
NArgsRange m_num_args_range{1, 1};
// Bit field of bool values. Set default value in ctor.
Expand Down
25 changes: 25 additions & 0 deletions test/test_actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,28 @@ TEST_CASE("Users can run actions on parameterless optional arguments" *
}
}
}

TEST_CASE("Users can add multiple actions and they are all run" *
test_suite("actions")) {
argparse::ArgumentParser program("test");

GIVEN("a flag argument with two counting actions") {
int count = 0;
program.add_argument("-V", "--verbose")
.action([&](const auto &) { ++count; })
.action([&](const auto &) { ++count; })
.append()
.default_value(false)
.implicit_value(true)
.nargs(0);

WHEN("the flag is parsed") {
program.parse_args({"test", "-V"});

THEN("the count increments twice") {
REQUIRE(program.get<bool>("-V"));
REQUIRE(count == 2);
}
}
}
}
47 changes: 47 additions & 0 deletions test/test_scan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -426,3 +426,50 @@ TEST_CASE_TEMPLATE("Parse floating-point argument of fixed format" *
std::invalid_argument);
}
}

TEST_CASE("Test that scan also works with a custom action" *
test_suite("scan")) {

GIVEN("an argument with scan followed by a custom action") {
argparse::ArgumentParser program("test");
int res;
program.add_argument("--int").scan<'i', int>().action([&](const auto &s) {res = std::stoi(s);});

WHEN("the argument is parsed") {

SUBCASE("with a valid value") {
program.parse_args({"./test.exe", "--int", "3"});
THEN("the value is stored") {
REQUIRE(res == 3);
}
}

SUBCASE("with an invalid value") {
REQUIRE_THROWS_AS(program.parse_args({"./test.exe", "--int", "XXX"}),
std::invalid_argument);
}
}
}

GIVEN("an argument with a custom action followed by scan") {
argparse::ArgumentParser program("test");
int res;
program.add_argument("--int").action([&](const auto &s) {res = std::stoi(s);}).scan<'i', int>();

WHEN("the argument is parsed") {

SUBCASE("with a valid value") {
program.parse_args({"./test.exe", "--int", "3"});
THEN("the value is stored") {
REQUIRE(res == 3);
}
}

SUBCASE("with an invalid value") {
REQUIRE_THROWS_AS(program.parse_args({"./test.exe", "--int", "XXX"}),
std::invalid_argument);
}
}
}

}
35 changes: 35 additions & 0 deletions test/test_store_into.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,3 +286,38 @@ TEST_CASE("Test store_into(set of string), default value, multi valued, specifie
}
}

TEST_CASE("Test store_into(int) still works with a custom action" *
test_suite("store_into")) {

GIVEN("an argument with store_into followed by a custom action ") {
argparse::ArgumentParser program("test");
int res;
std::string string_res;
program.add_argument("--int").store_into(res).action([&](const auto &s) {string_res.append(s);});

WHEN("the argument is parsed") {
program.parse_args({"./test.exe", "--int", "3"});
THEN("the value is stored and the action was executed") {
REQUIRE(res == 3);
REQUIRE(string_res == "3");
}
}
}

GIVEN("an argument with a custom action followed by store_into")
{
argparse::ArgumentParser program("test");
int res;
std::string string_res;
program.add_argument("--int").action([&](const auto &s) {string_res.append(s);}).store_into(res);

WHEN("the argument is parsed") {
program.parse_args({"./test.exe", "--int", "3"});
THEN("the value is stored and the action was executed") {
REQUIRE(res == 3);
REQUIRE(string_res == "3");
}
}
}
}

0 comments on commit 22b54b8

Please sign in to comment.