Skip to content

Commit

Permalink
Closes #260
Browse files Browse the repository at this point in the history
  • Loading branch information
p-ranav committed Nov 5, 2023
1 parent 85fda4a commit 26e324b
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 34 deletions.
45 changes: 38 additions & 7 deletions include/argparse/argparse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -906,7 +906,6 @@ class Argument {
}

std::size_t get_arguments_length() const {

std::size_t names_size = std::accumulate(
std::begin(m_names), std::end(m_names), std::size_t(0),
[](const auto &sum, const auto &s) { return sum + s.size(); });
Expand Down Expand Up @@ -1362,15 +1361,14 @@ class ArgumentParser {
explicit ArgumentParser(std::string program_name = {},
std::string version = "1.0",
default_arguments add_args = default_arguments::all,
bool exit_on_default_arguments = true,
std::ostream &os = std::cout)
bool exit_on_default_arguments = true)
: m_program_name(std::move(program_name)), m_version(std::move(version)),
m_exit_on_default_arguments(exit_on_default_arguments),
m_parser_path(m_program_name) {
if ((add_args & default_arguments::help) == default_arguments::help) {
add_argument("-h", "--help")
.action([&](const auto & /*unused*/) {
os << help().str();
.action([this](const auto & /*unused*/) {
std::cout << help().str();
if (m_exit_on_default_arguments) {
std::exit(0);
}
Expand All @@ -1379,11 +1377,12 @@ class ArgumentParser {
.help("shows help message and exits")
.implicit_value(true)
.nargs(0);
m_default_help_argument_added = true;
}
if ((add_args & default_arguments::version) == default_arguments::version) {
add_argument("-v", "--version")
.action([&](const auto & /*unused*/) {
os << m_version << std::endl;
.action([this](const auto & /*unused*/) {
std::cout << m_version << std::endl;
if (m_exit_on_default_arguments) {
std::exit(0);
}
Expand All @@ -1392,6 +1391,7 @@ class ArgumentParser {
.help("prints version information and exits")
.implicit_value(true)
.nargs(0);
m_default_version_argument_added = true;
}
}

Expand All @@ -1400,6 +1400,9 @@ class ArgumentParser {

ArgumentParser(const ArgumentParser &other)
: m_program_name(other.m_program_name), m_version(other.m_version),
m_default_help_argument_added(other.m_default_help_argument_added),
m_default_version_argument_added(
other.m_default_version_argument_added),
m_description(other.m_description), m_epilog(other.m_epilog),
m_exit_on_default_arguments(other.m_exit_on_default_arguments),
m_prefix_chars(other.m_prefix_chars),
Expand All @@ -1415,6 +1418,32 @@ class ArgumentParser {
it != std::end(m_optional_arguments); ++it) {
index_argument(it);
}

// Redefine help action
if (m_default_help_argument_added) {
auto help_argument = m_argument_map["--help"];
help_argument->action([this](const auto & /*unused*/) {
std::cout << help().str();
if (m_exit_on_default_arguments) {
std::exit(0);
}
});
}

if (m_default_version_argument_added) {
add_argument("-v", "--version")
.action([this](const auto & /*unused*/) {
std::cout << m_version << std::endl;
if (m_exit_on_default_arguments) {
std::exit(0);
}
})
.default_value(false)
.help("prints version information and exits")
.implicit_value(true)
.nargs(0);
}

for (auto it = std::begin(m_subparsers); it != std::end(m_subparsers);
++it) {
m_subparser_map.insert_or_assign(it->get().m_program_name, it);
Expand Down Expand Up @@ -2107,6 +2136,8 @@ class ArgumentParser {

std::string m_program_name;
std::string m_version;
bool m_default_help_argument_added{false};
bool m_default_version_argument_added{false};
std::string m_description;
std::string m_epilog;
bool m_exit_on_default_arguments = true;
Expand Down
1 change: 0 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ file(GLOB ARGPARSE_TEST_SOURCES
test_repr.cpp
test_required_arguments.cpp
test_scan.cpp
test_stringstream.cpp
test_value_semantics.cpp
test_version.cpp
test_subparsers.cpp
Expand Down
122 changes: 116 additions & 6 deletions test/test_copy_constructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,127 @@ import argparse;

using doctest::test_suite;

TEST_CASE("Parse positional arguments using a copy of an ArgumentParser" * test_suite("vector")) {
TEST_CASE("Parse positional arguments using a copy of an ArgumentParser" *
test_suite("vector")) {

auto maker = []() {
argparse::ArgumentParser program("test");
program.add_argument("first");
program.add_argument("second");
argparse::ArgumentParser program("test");
program.add_argument("first");
program.add_argument("second").nargs(2);

return program;
return program;
};

auto program = maker();

program.parse_args({"test", "rocket.mesh", "thrust_profile.csv"});
REQUIRE_NOTHROW(program.parse_args(
{"test", "rocket.mesh", "thrust_profile.csv", "config.json"}));

auto first = program.get<std::string>("first");
REQUIRE(first == "rocket.mesh");
auto second = program.get<std::vector<std::string>>("second");
REQUIRE(second.size() == 2);
REQUIRE(second[0] == "thrust_profile.csv");
REQUIRE(second[1] == "config.json");
}

TEST_CASE("Parse optional arguments using a copy of an ArgumentParser" *
test_suite("vector")) {

auto maker = []() {
argparse::ArgumentParser program("test");
program.add_argument("--first");
program.add_argument("--second").nargs(2);

return program;
};

auto program = maker();

REQUIRE_NOTHROW(
program.parse_args({"test", "--first", "rocket.mesh", "--second",
"thrust_profile.csv", "config.json"}));

auto first = program.get<std::string>("--first");
REQUIRE(first == "rocket.mesh");
auto second = program.get<std::vector<std::string>>("--second");
REQUIRE(second.size() == 2);
REQUIRE(second[0] == "thrust_profile.csv");
REQUIRE(second[1] == "config.json");
}

TEST_CASE("Segmentation fault on help (Issue #260)") {

struct SubparserContainer {
argparse::ArgumentParser parser;
};

auto get_container = []() {
SubparserContainer *container = nullptr;
if (container == nullptr) {
argparse::ArgumentParser parser("subcommand", "1.0",
argparse::default_arguments::all, false);
parser.add_description("Example");
container = new SubparserContainer{parser};
}
return container;
};

argparse::ArgumentParser program("program");
auto *container = get_container();
program.add_subparser(container->parser);

std::ostringstream oss;
std::streambuf *p_cout_streambuf = std::cout.rdbuf();
std::cout.rdbuf(oss.rdbuf());

program.parse_args({"program", "subcommand", "-h"});

std::cout.rdbuf(p_cout_streambuf); // restore

auto cmdline_output = oss.str();
REQUIRE(cmdline_output.size() > 0);
REQUIRE(cmdline_output.find("shows help message and exits") !=
std::string::npos);
}

TEST_CASE("Segmentation fault on custom help (Issue #260)") {

struct SubparserContainer {
argparse::ArgumentParser parser;
};

auto get_container = []() {
SubparserContainer *container = nullptr;
if (container == nullptr) {
argparse::ArgumentParser parser("subcommand", "1.0",
argparse::default_arguments::none, false);
parser.add_description("Example");
std::string temporary{"temp+string"};
parser.add_argument("-h", "--help")
.flag()
.nargs(0)
.action(
[&](const auto &) -> void { std::cout << temporary << "\n"; });

container = new SubparserContainer{parser};
}
return container;
};

argparse::ArgumentParser program("program");
auto *container = get_container();
program.add_subparser(container->parser);

std::ostringstream oss;
std::streambuf *p_cout_streambuf = std::cout.rdbuf();
std::cout.rdbuf(oss.rdbuf());

program.parse_args({"program", "subcommand", "-h"});

std::cout.rdbuf(p_cout_streambuf); // restore

auto cmdline_output = oss.str();
REQUIRE(cmdline_output.size() > 0);
REQUIRE(cmdline_output.find("temp+string") != std::string::npos);
}
20 changes: 0 additions & 20 deletions test/test_stringstream.cpp

This file was deleted.

0 comments on commit 26e324b

Please sign in to comment.