From 95c77095e38e175746cd458a4c4f4ba21d6fda4f Mon Sep 17 00:00:00 2001 From: HuangZonghao Date: Fri, 29 Apr 2022 00:45:45 -0400 Subject: [PATCH 1/5] Parse command line input wrapped in a string This is the simplest implementation of the functionality described in the short description. This patch only deals with the arguments seperated by whitespaces, and process them as is, without any shell processing like expanding wildcards or replacing variables. --- include/cxxopts.hpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/include/cxxopts.hpp b/include/cxxopts.hpp index b957956b..959f2f11 100644 --- a/include/cxxopts.hpp +++ b/include/cxxopts.hpp @@ -1772,6 +1772,9 @@ namespace cxxopts ParseResult parse(int argc, const char* const* argv); + ParseResult + parse(std::string argv_str); + OptionAdder add_options(std::string group = ""); @@ -2284,6 +2287,30 @@ Options::parse(int argc, const char* const* argv) return parser.parse(argc, argv); } +inline +ParseResult +Options::parse(std::string argv_str) +{ + std::vector tokens; + std::string token; + std::istringstream iss(argv_str); + while (std::getline(iss, token, ' ')) + { + tokens.push_back(token); + } + + const char** argv = new const char*[tokens.size()]; + for (int i = 0; i < tokens.size(); ++i) { + argv[i] = tokens[i].c_str(); + } + + OptionParser parser(*m_options, m_positional, m_allow_unrecognised); + + auto parse_result = parser.parse(tokens.size(), argv); + delete argv; + return parse_result; +} + inline ParseResult OptionParser::parse(int argc, const char* const* argv) { From 4f7ab890fa7b6bce7db8495c0349737dcb7ce20d Mon Sep 17 00:00:00 2001 From: HuangZonghao Date: Sat, 30 Apr 2022 23:53:42 -0400 Subject: [PATCH 2/5] Update example code and readme for string parsing --- README.md | 10 ++++++++++ src/example.cpp | 20 ++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ede30ef4..a26b77f9 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,16 @@ To parse the command line do: auto result = options.parse(argc, argv); ``` +Or if you have all the arguments of `argv` in a `std::string`, separated by +white spaces, do: + +```cpp +auto result = options.parse(argv_str); +``` + +Note the support for string parsing is limited. No bash variable replacement and +wild card expansion. + To retrieve an option use `result.count("option")` to get the number of times it appeared, and diff --git a/src/example.cpp b/src/example.cpp index 420220e1..caacd54d 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -27,7 +27,7 @@ THE SOFTWARE. #include "cxxopts.hpp" void -parse(int argc, const char* argv[]) +parse(int argc, const char* argv[], bool parse_as_string = false) { try { @@ -77,7 +77,20 @@ parse(int argc, const char* argv[]) options.parse_positional({"input", "output", "positional"}); - auto result = options.parse(argc, argv); + cxxopts::ParseResult result; + if (parse_as_string) + { + std::string argv_str; + for (int i = 0; i < argc; ++i) + { + argv_str += std::string(argv[i]) + std::string(" "); + } + result = options.parse(argv_str); + } + else + { + result = options.parse(argc, argv); + } if (result.count("help")) { @@ -185,5 +198,8 @@ int main(int argc, const char* argv[]) { parse(argc, argv); + // Parse argv as string + parse(argc, argv, true); + return 0; } From fb502603816f67f64798c404264e8afca2612444 Mon Sep 17 00:00:00 2001 From: HuangZonghao Date: Mon, 2 May 2022 14:32:39 -0400 Subject: [PATCH 3/5] Use constref for string, smart pointer for char array, and address compilation warnings of 95c7709 --- include/cxxopts.hpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/include/cxxopts.hpp b/include/cxxopts.hpp index 959f2f11..e4a40991 100644 --- a/include/cxxopts.hpp +++ b/include/cxxopts.hpp @@ -1773,7 +1773,7 @@ namespace cxxopts parse(int argc, const char* const* argv); ParseResult - parse(std::string argv_str); + parse(const std::string& argv_str); OptionAdder add_options(std::string group = ""); @@ -2289,7 +2289,7 @@ Options::parse(int argc, const char* const* argv) inline ParseResult -Options::parse(std::string argv_str) +Options::parse(const std::string& argv_str) { std::vector tokens; std::string token; @@ -2299,15 +2299,14 @@ Options::parse(std::string argv_str) tokens.push_back(token); } - const char** argv = new const char*[tokens.size()]; - for (int i = 0; i < tokens.size(); ++i) { + std::unique_ptr argv(new const char*[tokens.size()]); + for (std::size_t i = 0; i < tokens.size(); ++i) { argv[i] = tokens[i].c_str(); } OptionParser parser(*m_options, m_positional, m_allow_unrecognised); - auto parse_result = parser.parse(tokens.size(), argv); - delete argv; + auto parse_result = parser.parse(static_cast(tokens.size()), argv.get()); return parse_result; } From 6c6deabc89713a9252fb46b0cfdc57739f7e0f89 Mon Sep 17 00:00:00 2001 From: HuangZonghao Date: Tue, 3 May 2022 04:15:49 -0400 Subject: [PATCH 4/5] Add test cases for string parsing --- test/options.cpp | 238 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 209 insertions(+), 29 deletions(-) diff --git a/test/options.cpp b/test/options.cpp index 3398b70a..5d2b09f2 100644 --- a/test/options.cpp +++ b/test/options.cpp @@ -21,6 +21,8 @@ class Argv { m_args.push_back(std::move(ptr)); m_argv.get()[i] = m_args.back().get(); + m_argv_str += std::string(m_argv.get()[i]) + std::string(" "); + ++iter; ++i; } @@ -30,6 +32,10 @@ class Argv { return m_argv.get(); } + const std::string& argv_str() const { + return m_argv_str; + } + int argc() const { return m_argc; } @@ -39,6 +45,7 @@ class Argv { std::vector> m_args{}; std::unique_ptr m_argv; int m_argc; + std::string m_argv_str; }; TEST_CASE("Basic options", "[options]") @@ -73,7 +80,6 @@ TEST_CASE("Basic options", "[options]") auto argc = argv.argc(); auto result = options.parse(argc, actual_argv); - CHECK(result.count("long") == 1); CHECK(result.count("s") == 1); CHECK(result.count("value") == 1); @@ -83,19 +89,36 @@ TEST_CASE("Basic options", "[options]") CHECK(result.count("6") == 1); CHECK(result.count("p") == 2); CHECK(result.count("space") == 2); - auto& arguments = result.arguments(); REQUIRE(arguments.size() == 7); CHECK(arguments[0].key() == "long"); CHECK(arguments[0].value() == "true"); CHECK(arguments[0].as() == true); - CHECK(arguments[1].key() == "short"); CHECK(arguments[2].key() == "value"); CHECK(arguments[3].key() == "av"); - CHECK_THROWS_AS(result["nothing"].as(), cxxopts::option_has_no_value_exception&); + auto str_result = options.parse(argv.argv_str()); + CHECK(str_result.count("long") == 1); + CHECK(str_result.count("s") == 1); + CHECK(str_result.count("value") == 1); + CHECK(str_result.count("a") == 1); + CHECK(str_result["value"].as() == "value"); + CHECK(str_result["a"].as() == "b"); + CHECK(str_result.count("6") == 1); + CHECK(str_result.count("p") == 2); + CHECK(str_result.count("space") == 2); + auto& str_arguments = str_result.arguments(); + REQUIRE(str_arguments.size() == 7); + CHECK(str_arguments[0].key() == "long"); + CHECK(str_arguments[0].value() == "true"); + CHECK(str_arguments[0].as() == true); + CHECK(str_arguments[1].key() == "short"); + CHECK(str_arguments[2].key() == "value"); + CHECK(str_arguments[3].key() == "av"); + CHECK_THROWS_AS(str_result["nothing"].as(), cxxopts::option_has_no_value_exception&); + CHECK(options.program() == "tester"); } @@ -112,10 +135,13 @@ TEST_CASE("Short options", "[options]") auto argc = argv.argc(); auto result = options.parse(argc, actual_argv); - CHECK(result.count("a") == 1); CHECK(result["a"].as() == "value"); + auto str_result = options.parse(argv.argv_str()); + CHECK(str_result.count("a") == 1); + CHECK(str_result["a"].as() == "value"); + REQUIRE_THROWS_AS(options.add_options()("", "nothing option"), cxxopts::invalid_option_format_error&); } @@ -155,10 +181,16 @@ TEST_CASE("All positional", "[positional]") options.parse_positional(pos_names.begin(), pos_names.end()); auto result = options.parse(argc, argv); - CHECK(result.unmatched().size() == 0); REQUIRE(positional.size() == 3); + CHECK(positional[0] == "a"); + CHECK(positional[1] == "b"); + CHECK(positional[2] == "c"); + positional.clear(); + auto str_result = options.parse(av.argv_str()); + CHECK(str_result.unmatched().size() == 0); + REQUIRE(positional.size() == 3); CHECK(positional[0] == "a"); CHECK(positional[1] == "b"); CHECK(positional[2] == "c"); @@ -183,17 +215,24 @@ TEST_CASE("Some positional explicit", "[positional]") auto argc = av.argc(); auto result = options.parse(argc, argv); - CHECK(result.unmatched().size() == 0); CHECK(result.count("output")); CHECK(result["input"].as() == "b"); CHECK(result["output"].as() == "a"); - auto& positional = result["positional"].as>(); - REQUIRE(positional.size() == 2); CHECK(positional[0] == "c"); CHECK(positional[1] == "d"); + + auto str_result = options.parse(av.argv_str()); + CHECK(str_result.unmatched().size() == 0); + CHECK(str_result.count("output")); + CHECK(str_result["input"].as() == "b"); + CHECK(str_result["output"].as() == "a"); + auto& str_positional = str_result["positional"].as>(); + REQUIRE(str_positional.size() == 2); + CHECK(str_positional[0] == "c"); + CHECK(str_positional[1] == "d"); } TEST_CASE("No positional with extras", "[positional]") @@ -212,9 +251,12 @@ TEST_CASE("No positional with extras", "[positional]") auto old_argc = argc; auto result = options.parse(argc, argv); - auto& unmatched = result.unmatched(); CHECK((unmatched == std::vector{"a", "b", "c", "d"})); + + auto str_result = options.parse(av.argv_str()); + auto& str_unmatched = str_result.unmatched(); + CHECK((str_unmatched == std::vector{"a", "b", "c", "d"})); } TEST_CASE("Positional not valid", "[positional]") { @@ -231,6 +273,7 @@ TEST_CASE("Positional not valid", "[positional]") { auto argc = av.argc(); CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::option_not_exists_exception&); + CHECK_THROWS_AS(options.parse(av.argv_str()), cxxopts::option_not_exists_exception&); } TEST_CASE("Positional with empty arguments", "[positional]") { @@ -251,11 +294,17 @@ TEST_CASE("Positional with empty arguments", "[positional]") { auto result = options.parse(argc, argv); auto actual = result["programArgs"].as>(); - REQUIRE(result.count("program") == 1); REQUIRE(result["program"].as() == "someProgram"); REQUIRE(result.count("programArgs") == expected.size()); REQUIRE(actual == expected); + + auto str_result = options.parse(av.argv_str()); + auto str_actual = str_result["programArgs"].as>(); + REQUIRE(str_result.count("program") == 1); + REQUIRE(str_result["program"].as() == "someProgram"); + REQUIRE(str_result.count("programArgs") == expected.size()); + REQUIRE(str_actual == expected); } TEST_CASE("Empty with implicit value", "[implicit]") @@ -271,9 +320,12 @@ TEST_CASE("Empty with implicit value", "[implicit]") auto argc = av.argc(); auto result = options.parse(argc, argv); - REQUIRE(result.count("implicit") == 1); REQUIRE(result["implicit"].as() == ""); + + auto str_result = options.parse(av.argv_str()); + REQUIRE(str_result.count("implicit") == 1); + REQUIRE(str_result["implicit"].as() == ""); } TEST_CASE("Boolean without implicit value", "[implicit]") @@ -290,6 +342,7 @@ TEST_CASE("Boolean without implicit value", "[implicit]") auto argc = av.argc(); CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::missing_argument_exception&); + CHECK_THROWS_AS(options.parse(av.argv_str()), cxxopts::missing_argument_exception&); } SECTION("With equal-separated true") { @@ -301,6 +354,10 @@ TEST_CASE("Boolean without implicit value", "[implicit]") auto result = options.parse(argc, argv); CHECK(result.count("bool") == 1); CHECK(result["bool"].as() == true); + + auto str_result = options.parse(av.argv_str()); + CHECK(str_result.count("bool") == 1); + CHECK(str_result["bool"].as() == true); } SECTION("With equal-separated false") { @@ -312,6 +369,10 @@ TEST_CASE("Boolean without implicit value", "[implicit]") auto result = options.parse(argc, argv); CHECK(result.count("bool") == 1); CHECK(result["bool"].as() == false); + + auto str_result = options.parse(av.argv_str()); + CHECK(str_result.count("bool") == 1); + CHECK(str_result["bool"].as() == false); } SECTION("With space-separated true") { @@ -323,6 +384,10 @@ TEST_CASE("Boolean without implicit value", "[implicit]") auto result = options.parse(argc, argv); CHECK(result.count("bool") == 1); CHECK(result["bool"].as() == true); + + auto str_result = options.parse(av.argv_str()); + CHECK(str_result.count("bool") == 1); + CHECK(str_result["bool"].as() == true); } SECTION("With space-separated false") { @@ -334,6 +399,10 @@ TEST_CASE("Boolean without implicit value", "[implicit]") auto result = options.parse(argc, argv); CHECK(result.count("bool") == 1); CHECK(result["bool"].as() == false); + + auto str_result = options.parse(av.argv_str()); + CHECK(str_result.count("bool") == 1); + CHECK(str_result["bool"].as() == false); } } @@ -355,11 +424,18 @@ TEST_CASE("Default values", "[default]") auto result = options.parse(argc, argv); CHECK(result.count("default") == 0); CHECK(result["default"].as() == 42); - auto& v = result["vector"].as>(); REQUIRE(v.size() == 2); CHECK(v[0] == 1); CHECK(v[1] == 4); + + auto str_result = options.parse(av.argv_str()); + CHECK(str_result.count("default") == 0); + CHECK(str_result["default"].as() == 42); + auto& str_v = str_result["vector"].as>(); + REQUIRE(str_v.size() == 2); + CHECK(str_v[0] == 1); + CHECK(str_v[1] == 4); } SECTION("When values provided") { @@ -371,6 +447,10 @@ TEST_CASE("Default values", "[default]") auto result = options.parse(argc, argv); CHECK(result.count("default") == 1); CHECK(result["default"].as() == 5); + + auto str_result = options.parse(av.argv_str()); + CHECK(str_result.count("default") == 1); + CHECK(str_result["default"].as() == 5); } } @@ -390,6 +470,10 @@ TEST_CASE("Parse into a reference", "[reference]") auto result = options.parse(argc, argv); CHECK(result.count("ref") == 1); CHECK(value == 42); + + auto str_result = options.parse(av.argv_str()); + CHECK(str_result.count("ref") == 1); + CHECK(value == 42); } TEST_CASE("Integers", "[options]") @@ -405,9 +489,7 @@ TEST_CASE("Integers", "[options]") options.parse_positional("positional"); auto result = options.parse(argc, argv); - REQUIRE(result.count("positional") == 7); - auto& positional = result["positional"].as>(); REQUIRE(positional.size() == 7); CHECK(positional[0] == 5); @@ -417,6 +499,18 @@ TEST_CASE("Integers", "[options]") CHECK(positional[4] == 0xab); CHECK(positional[5] == 0xaf); CHECK(positional[6] == 0x0); + + auto str_result = options.parse(av.argv_str()); + REQUIRE(str_result.count("positional") == 7); + auto& str_positional = str_result["positional"].as>(); + REQUIRE(str_positional.size() == 7); + CHECK(str_positional[0] == 5); + CHECK(str_positional[1] == 6); + CHECK(str_positional[2] == -6); + CHECK(str_positional[3] == 0); + CHECK(str_positional[4] == 0xab); + CHECK(str_positional[5] == 0xaf); + CHECK(str_positional[6] == 0x0); } TEST_CASE("Leading zero integers", "[options]") @@ -431,16 +525,24 @@ TEST_CASE("Leading zero integers", "[options]") auto argc = av.argc(); options.parse_positional("positional"); - auto result = options.parse(argc, argv); + auto result = options.parse(argc, argv); REQUIRE(result.count("positional") == 4); - auto& positional = result["positional"].as>(); REQUIRE(positional.size() == 4); CHECK(positional[0] == 5); CHECK(positional[1] == 6); CHECK(positional[2] == 0xab); CHECK(positional[3] == 0x1); + + auto str_result = options.parse(av.argv_str()); + REQUIRE(str_result.count("positional") == 4); + auto& str_positional = str_result["positional"].as>(); + REQUIRE(str_positional.size() == 4); + CHECK(str_positional[0] == 5); + CHECK(str_positional[1] == 6); + CHECK(str_positional[2] == 0xab); + CHECK(str_positional[3] == 0x1); } TEST_CASE("Unsigned integers", "[options]") @@ -456,6 +558,7 @@ TEST_CASE("Unsigned integers", "[options]") options.parse_positional("positional"); CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::argument_incorrect_type&); + CHECK_THROWS_AS(options.parse(av.argv_str()), cxxopts::argument_incorrect_type&); } TEST_CASE("Integer bounds", "[integer]") @@ -473,15 +576,22 @@ TEST_CASE("Integer bounds", "[integer]") options.parse_positional("positional"); auto result = options.parse(argc, argv); - REQUIRE(result.count("positional") == 5); - auto& positional = result["positional"].as>(); CHECK(positional[0] == 127); CHECK(positional[1] == -128); CHECK(positional[2] == 0x7f); CHECK(positional[3] == -0x80); CHECK(positional[4] == 0x7e); + + auto str_result = options.parse(av.argv_str()); + REQUIRE(str_result.count("positional") == 5); + auto& str_positional = str_result["positional"].as>(); + CHECK(str_positional[0] == 127); + CHECK(str_positional[1] == -128); + CHECK(str_positional[2] == 0x7f); + CHECK(str_positional[3] == -0x80); + CHECK(str_positional[4] == 0x7e); } } @@ -515,6 +625,7 @@ TEST_CASE("Integer overflow", "[options]") options.parse_positional("positional"); CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::argument_incorrect_type&); + CHECK_THROWS_AS(options.parse(av.argv_str()), cxxopts::argument_incorrect_type&); int integer = 0; CHECK_THROWS_AS((integer_parser("23423423423", integer)), cxxopts::argument_incorrect_type&); @@ -534,18 +645,26 @@ TEST_CASE("Floats", "[options]") auto argc = av.argc(); options.parse_positional("positional"); - auto result = options.parse(argc, argv); + auto result = options.parse(argc, argv); REQUIRE(result.count("double") == 1); REQUIRE(result.count("positional") == 4); - CHECK(result["double"].as() == 0.5); - auto& positional = result["positional"].as>(); CHECK(positional[0] == 4); CHECK(positional[1] == -4); CHECK(positional[2] == 1.5e6); CHECK(positional[3] == -1.5e6); + + auto str_result = options.parse(av.argv_str()); + REQUIRE(str_result.count("double") == 1); + REQUIRE(str_result.count("positional") == 4); + CHECK(str_result["double"].as() == 0.5); + auto& str_positional = str_result["positional"].as>(); + CHECK(str_positional[0] == 4); + CHECK(str_positional[1] == -4); + CHECK(str_positional[2] == 1.5e6); + CHECK(str_positional[3] == -1.5e6); } TEST_CASE("Invalid integers", "[integer]") { @@ -560,6 +679,7 @@ TEST_CASE("Invalid integers", "[integer]") { options.parse_positional("positional"); CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::argument_incorrect_type&); + CHECK_THROWS_AS(options.parse(av.argv_str()), cxxopts::argument_incorrect_type&); } TEST_CASE("Booleans", "[boolean]") { @@ -584,7 +704,6 @@ TEST_CASE("Booleans", "[boolean]") { auto argc = av.argc(); auto result = options.parse(argc, argv); - REQUIRE(result.count("bool") == 1); REQUIRE(result.count("debug") == 1); REQUIRE(result.count("timing") == 1); @@ -593,7 +712,6 @@ TEST_CASE("Booleans", "[boolean]") { REQUIRE(result.count("noExplicitDefault") == 0); REQUIRE(result.count("defaultTrue") == 0); REQUIRE(result.count("defaultFalse") == 0); - CHECK(result["bool"].as() == false); CHECK(result["debug"].as() == true); CHECK(result["timing"].as() == true); @@ -602,8 +720,26 @@ TEST_CASE("Booleans", "[boolean]") { CHECK(result["noExplicitDefault"].as() == false); CHECK(result["defaultTrue"].as() == true); CHECK(result["defaultFalse"].as() == false); - REQUIRE(result.count("others") == 1); + + auto str_result = options.parse(av.argv_str()); + REQUIRE(str_result.count("bool") == 1); + REQUIRE(str_result.count("debug") == 1); + REQUIRE(str_result.count("timing") == 1); + REQUIRE(str_result.count("verbose") == 1); + REQUIRE(str_result.count("dry-run") == 1); + REQUIRE(str_result.count("noExplicitDefault") == 0); + REQUIRE(str_result.count("defaultTrue") == 0); + REQUIRE(str_result.count("defaultFalse") == 0); + CHECK(str_result["bool"].as() == false); + CHECK(str_result["debug"].as() == true); + CHECK(str_result["timing"].as() == true); + CHECK(str_result["verbose"].as() == true); + CHECK(str_result["dry-run"].as() == false); + CHECK(str_result["noExplicitDefault"].as() == false); + CHECK(str_result["defaultTrue"].as() == true); + CHECK(str_result["defaultFalse"].as() == false); + REQUIRE(str_result.count("others") == 1); } TEST_CASE("std::vector", "[vector]") { @@ -618,7 +754,14 @@ TEST_CASE("std::vector", "[vector]") { auto argc = av.argc(); options.parse(argc, argv); + REQUIRE(vector.size() == 4); + CHECK(vector[0] == 1); + CHECK(vector[1] == -2.1); + CHECK(vector[2] == 3); + CHECK(vector[3] == 4.5); + vector.clear(); + options.parse(av.argv_str()); REQUIRE(vector.size() == 4); CHECK(vector[0] == 1); CHECK(vector[1] == -2.1); @@ -639,7 +782,10 @@ TEST_CASE("std::optional", "[optional]") { auto argc = av.argc(); options.parse(argc, argv); + REQUIRE(optional.has_value()); + CHECK(*optional == "foo"); + options.parse(av.argv_str()); REQUIRE(optional.has_value()); CHECK(*optional == "foo"); } @@ -666,6 +812,7 @@ TEST_CASE("Unrecognised options", "[options]") { SECTION("Default behaviour") { CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::option_not_exists_exception&); + CHECK_THROWS_AS(options.parse(av.argv_str()), cxxopts::option_not_exists_exception&); } SECTION("After allowing unrecognised options") { @@ -673,6 +820,10 @@ TEST_CASE("Unrecognised options", "[options]") { auto result = options.parse(argc, argv); auto& unmatched = result.unmatched(); CHECK((unmatched == std::vector{"--unknown", "-u", "--another_unknown", "-a"})); + + auto str_result = options.parse(av.argv_str()); + auto& str_unmatched = str_result.unmatched(); + CHECK((str_unmatched == std::vector{"--unknown", "-u", "--another_unknown", "-a"})); } } @@ -693,11 +844,13 @@ TEST_CASE("Allow bad short syntax", "[options]") { SECTION("Default behaviour") { CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::option_syntax_exception&); + CHECK_THROWS_AS(options.parse(av.argv_str()), cxxopts::option_syntax_exception&); } SECTION("After allowing unrecognised options") { options.allow_unrecognised_options(); CHECK_NOTHROW(options.parse(argc, argv)); + CHECK_NOTHROW(options.parse(av.argv_str())); REQUIRE(argc == 2); CHECK_THAT(argv[1], Catch::Equals("-some_bad_short")); } @@ -716,6 +869,7 @@ TEST_CASE("Invalid option syntax", "[options]") { SECTION("Default behaviour") { CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::option_syntax_exception&); + CHECK_THROWS_AS(options.parse(av.argv_str()), cxxopts::option_syntax_exception&); } } @@ -735,6 +889,7 @@ TEST_CASE("Options empty", "[options]") { CHECK(options.groups().empty()); CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::option_not_exists_exception&); + CHECK_THROWS_AS(options.parse(argv_.argv_str()), cxxopts::option_not_exists_exception&); } TEST_CASE("Initializer list with group", "[options]") { @@ -762,8 +917,8 @@ TEST_CASE("Initializer list with group", "[options]") { }); auto** actual_argv = argv.argv(); auto argc = argv.argc(); - auto result = options.parse(argc, actual_argv); + auto result = options.parse(argc, actual_argv); CHECK(options.groups().size() == 2); CHECK(result.count("address") == 1); CHECK(result.count("port") == 1); @@ -772,6 +927,16 @@ TEST_CASE("Initializer list with group", "[options]") { CHECK(result["address"].as() == "10.0.0.1"); CHECK(result["port"].as() == "8000"); CHECK(result["test"].as() == true); + + auto str_result = options.parse(argv.argv_str()); + CHECK(options.groups().size() == 2); + CHECK(str_result.count("address") == 1); + CHECK(str_result.count("port") == 1); + CHECK(str_result.count("test") == 1); + CHECK(str_result.count("help") == 0); + CHECK(str_result["address"].as() == "10.0.0.1"); + CHECK(str_result["port"].as() == "8000"); + CHECK(str_result["test"].as() == true); } TEST_CASE("Option add with add_option(string, Option)", "[options]") { @@ -791,8 +956,8 @@ TEST_CASE("Option add with add_option(string, Option)", "[options]") { }); auto argc = argv_.argc(); auto** argv = argv_.argv(); - auto result = options.parse(argc, argv); + auto result = options.parse(argc, argv); CHECK(result.arguments().size()==2); CHECK(options.groups().size() == 2); CHECK(result.count("address") == 0); @@ -800,6 +965,15 @@ TEST_CASE("Option add with add_option(string, Option)", "[options]") { CHECK(result.count("test") == 1); CHECK(result["aggregate"].as() == 4); CHECK(result["test"].as() == 5); + + auto str_result = options.parse(argv_.argv_str()); + CHECK(str_result.arguments().size()==2); + CHECK(options.groups().size() == 2); + CHECK(str_result.count("address") == 0); + CHECK(str_result.count("aggregate") == 1); + CHECK(str_result.count("test") == 1); + CHECK(str_result["aggregate"].as() == 4); + CHECK(str_result["test"].as() == 5); } TEST_CASE("Const array", "[const]") { @@ -823,12 +997,18 @@ TEST_CASE("Parameter follow option", "[parameter]") { auto argc = av.argc(); auto result = options.parse(argc, argv); - REQUIRE(result.count("job") == 4); - auto job_values = result["job"].as>(); CHECK(job_values[0] == 9); CHECK(job_values[1] == 7); CHECK(job_values[2] == 10); CHECK(job_values[3] == 5); + + auto str_result = options.parse(av.argv_str()); + REQUIRE(str_result.count("job") == 4); + auto str_job_values = str_result["job"].as>(); + CHECK(str_job_values[0] == 9); + CHECK(str_job_values[1] == 7); + CHECK(str_job_values[2] == 10); + CHECK(str_job_values[3] == 5); } From ca9868407776eb859ef36c8b5e7207be6c542ed5 Mon Sep 17 00:00:00 2001 From: HuangZonghao Date: Tue, 3 May 2022 04:19:42 -0400 Subject: [PATCH 5/5] Revert changes made to example.cpp in 4f7ab89 --- src/example.cpp | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/example.cpp b/src/example.cpp index caacd54d..420220e1 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -27,7 +27,7 @@ THE SOFTWARE. #include "cxxopts.hpp" void -parse(int argc, const char* argv[], bool parse_as_string = false) +parse(int argc, const char* argv[]) { try { @@ -77,20 +77,7 @@ parse(int argc, const char* argv[], bool parse_as_string = false) options.parse_positional({"input", "output", "positional"}); - cxxopts::ParseResult result; - if (parse_as_string) - { - std::string argv_str; - for (int i = 0; i < argc; ++i) - { - argv_str += std::string(argv[i]) + std::string(" "); - } - result = options.parse(argv_str); - } - else - { - result = options.parse(argc, argv); - } + auto result = options.parse(argc, argv); if (result.count("help")) { @@ -198,8 +185,5 @@ int main(int argc, const char* argv[]) { parse(argc, argv); - // Parse argv as string - parse(argc, argv, true); - return 0; }