From 15e8a74e95ac0f1c45068f5db8ea31776d946b30 Mon Sep 17 00:00:00 2001 From: Kjetil Andresen Date: Tue, 11 Aug 2020 00:01:29 +0200 Subject: [PATCH] Support 'const char**' arguments in Options::parse (#250) `cxxopts` doesn't modify the contents of the argv strings. This changes the parse function to take a reference to a `const char**`. --- CHANGELOG.md | 1 + include/cxxopts.hpp | 16 ++++++------ src/example.cpp | 4 +-- test/options.cpp | 60 ++++++++++++++++++++++----------------------- 4 files changed, 41 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bddcff..16b5a99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ options. The project adheres to semantic versioning. * Fix duplicate default options when there is a short and long option. * Add `CXXOPTS_NO_EXCEPTIONS` to disable exceptions. * Fix char parsing for space and check for length. +* Change argument type in `Options::parse` from `char**` to `const char**`. ## 2.2 diff --git a/include/cxxopts.hpp b/include/cxxopts.hpp index ae95343..995b893 100644 --- a/include/cxxopts.hpp +++ b/include/cxxopts.hpp @@ -1211,7 +1211,7 @@ namespace cxxopts >, std::vector, bool allow_unrecognised, - int&, char**&); + int&, const char**&); size_t count(const std::string& o) const @@ -1251,7 +1251,7 @@ namespace cxxopts private: void - parse(int& argc, char**& argv); + parse(int& argc, const char**& argv); void add_to_option(const std::string& option, const std::string& arg); @@ -1274,7 +1274,7 @@ namespace cxxopts checked_parse_arg ( int argc, - char* argv[], + const char* argv[], int& current, const std::shared_ptr& value, const std::string& name @@ -1361,7 +1361,7 @@ namespace cxxopts } ParseResult - parse(int& argc, char**& argv); + parse(int& argc, const char**& argv); OptionAdder add_options(std::string group = ""); @@ -1620,7 +1620,7 @@ ParseResult::ParseResult > options, std::vector positional, bool allow_unrecognised, - int& argc, char**& argv + int& argc, const char**& argv ) : m_options(std::move(options)) , m_positional(std::move(positional)) @@ -1734,7 +1734,7 @@ void ParseResult::checked_parse_arg ( int argc, - char* argv[], + const char* argv[], int& current, const std::shared_ptr& value, const std::string& name @@ -1835,7 +1835,7 @@ Options::parse_positional(std::initializer_list options) inline ParseResult -Options::parse(int& argc, char**& argv) +Options::parse(int& argc, const char**& argv) { ParseResult result(m_options, m_positional, m_allow_unrecognised, argc, argv); return result; @@ -1843,7 +1843,7 @@ Options::parse(int& argc, char**& argv) inline void -ParseResult::parse(int& argc, char**& argv) +ParseResult::parse(int& argc, const char**& argv) { int current = 1; diff --git a/src/example.cpp b/src/example.cpp index 9b6ea88..0efb3a4 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -27,7 +27,7 @@ THE SOFTWARE. #include "cxxopts.hpp" void -parse(int argc, char* argv[]) +parse(int argc, const char* argv[]) { try { @@ -160,7 +160,7 @@ parse(int argc, char* argv[]) } } -int main(int argc, char* argv[]) +int main(int argc, const char* argv[]) { parse(argc, argv); diff --git a/test/options.cpp b/test/options.cpp index 5026ea0..0dddfe4 100644 --- a/test/options.cpp +++ b/test/options.cpp @@ -8,7 +8,7 @@ class Argv { public: Argv(std::initializer_list args) - : m_argv(new char*[args.size()]) + : m_argv(new const char*[args.size()]) , m_argc(static_cast(args.size())) { int i = 0; @@ -26,7 +26,7 @@ class Argv { } } - char** argv() const { + const char** argv() const { return m_argv.get(); } @@ -37,7 +37,7 @@ class Argv { private: std::vector> m_args; - std::unique_ptr m_argv; + std::unique_ptr m_argv; int m_argc; }; @@ -69,7 +69,7 @@ TEST_CASE("Basic options", "[options]") "--space", }); - char** actual_argv = argv.argv(); + auto** actual_argv = argv.argv(); auto argc = argv.argc(); auto result = options.parse(argc, actual_argv); @@ -125,7 +125,7 @@ TEST_CASE("No positional", "[positional]") Argv av({"tester", "a", "b", "def"}); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); auto result = options.parse(argc, argv); @@ -177,7 +177,7 @@ TEST_CASE("Some positional explicit", "[positional]") Argv av({"tester", "--output", "a", "b", "c", "d"}); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); auto result = options.parse(argc, argv); @@ -203,7 +203,7 @@ TEST_CASE("No positional with extras", "[positional]") Argv av({"extras", "--", "a", "b", "c", "d"}); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); auto old_argv = argv; @@ -226,7 +226,7 @@ TEST_CASE("Positional not valid", "[positional]") { Argv av({"foobar", "bar", "baz"}); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::option_not_exists_exception&); @@ -241,7 +241,7 @@ TEST_CASE("Empty with implicit value", "[implicit]") Argv av({"implicit", "--implicit="}); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); auto result = options.parse(argc, argv); @@ -260,7 +260,7 @@ TEST_CASE("Boolean without implicit value", "[implicit]") SECTION("When no value provided") { Argv av({"no_implicit", "--bool"}); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::missing_argument_exception&); @@ -269,7 +269,7 @@ TEST_CASE("Boolean without implicit value", "[implicit]") SECTION("With equal-separated true") { Argv av({"no_implicit", "--bool=true"}); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); auto result = options.parse(argc, argv); @@ -280,7 +280,7 @@ TEST_CASE("Boolean without implicit value", "[implicit]") SECTION("With equal-separated false") { Argv av({"no_implicit", "--bool=false"}); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); auto result = options.parse(argc, argv); @@ -291,7 +291,7 @@ TEST_CASE("Boolean without implicit value", "[implicit]") SECTION("With space-separated true") { Argv av({"no_implicit", "--bool", "true"}); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); auto result = options.parse(argc, argv); @@ -302,7 +302,7 @@ TEST_CASE("Boolean without implicit value", "[implicit]") SECTION("With space-separated false") { Argv av({"no_implicit", "--bool", "false"}); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); auto result = options.parse(argc, argv); @@ -323,7 +323,7 @@ TEST_CASE("Default values", "[default]") SECTION("Sets defaults") { Argv av({"implicit"}); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); auto result = options.parse(argc, argv); @@ -339,7 +339,7 @@ TEST_CASE("Default values", "[default]") SECTION("When values provided") { Argv av({"implicit", "--default", "5"}); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); auto result = options.parse(argc, argv); @@ -374,7 +374,7 @@ TEST_CASE("Integers", "[options]") Argv av({"ints", "--", "5", "6", "-6", "0", "0xab", "0xAf", "0x0"}); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); options.parse_positional("positional"); @@ -401,7 +401,7 @@ TEST_CASE("Leading zero integers", "[options]") Argv av({"ints", "--", "05", "06", "0x0ab", "0x0001"}); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); options.parse_positional("positional"); @@ -425,7 +425,7 @@ TEST_CASE("Unsigned integers", "[options]") Argv av({"ints", "--", "-2"}); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); options.parse_positional("positional"); @@ -504,7 +504,7 @@ TEST_CASE("Floats", "[options]") Argv av({"floats", "--double", "0.5", "--", "4", "-4", "1.5e6", "-1.5e6"}); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); options.parse_positional("positional"); @@ -529,7 +529,7 @@ TEST_CASE("Invalid integers", "[integer]") { Argv av({"ints", "--", "Ae"}); - char **argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); options.parse_positional("positional"); @@ -554,7 +554,7 @@ TEST_CASE("Booleans", "[boolean]") { Argv av({"booleans", "--bool=false", "--debug=true", "--timing", "--verbose=1", "--dry-run=0", "extra"}); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); auto result = options.parse(argc, argv); @@ -588,7 +588,7 @@ TEST_CASE("std::vector", "[vector]") { Argv av({"vector", "--vector", "1,-2.1,3,4.5"}); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); options.parse(argc, argv); @@ -609,7 +609,7 @@ TEST_CASE("std::optional", "[optional]") { Argv av({"optional", "--optional", "foo"}); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); options.parse(argc, argv); @@ -634,7 +634,7 @@ TEST_CASE("Unrecognised options", "[options]") { "--another_unknown", }); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); SECTION("Default behaviour") { @@ -661,7 +661,7 @@ TEST_CASE("Allow bad short syntax", "[options]") { "-some_bad_short", }); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); SECTION("Default behaviour") { @@ -684,7 +684,7 @@ TEST_CASE("Invalid option syntax", "[options]") { "--a", }); - char** argv = av.argv(); + auto** argv = av.argv(); auto argc = av.argc(); SECTION("Default behaviour") { @@ -704,7 +704,7 @@ TEST_CASE("Options empty", "[options]") { "--unknown" }); auto argc = argv_.argc(); - char** argv = argv_.argv(); + auto** argv = argv_.argv(); CHECK(options.groups().empty()); CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::option_not_exists_exception&); @@ -733,7 +733,7 @@ TEST_CASE("Initializer list with group", "[options]") { "8000", "-t", }); - char** actual_argv = argv.argv(); + auto** actual_argv = argv.argv(); auto argc = argv.argc(); auto result = options.parse(argc, actual_argv); @@ -763,7 +763,7 @@ TEST_CASE("Option add with add_option(string, Option)", "[options]") { "4" }); auto argc = argv_.argc(); - char** argv = argv_.argv(); + auto** argv = argv_.argv(); auto result = options.parse(argc, argv); CHECK(result.arguments().size()==2);