From 22ab9836d46477a825a83142a1bfa194f352d1b9 Mon Sep 17 00:00:00 2001 From: Baptiste Wicht Date: Sun, 26 Oct 2014 21:01:18 +0100 Subject: [PATCH 01/12] Add basic support for default options --- src/cxxopts.hpp | 87 ++++++++++++++++++++++++++++++++++++++++++++++++- src/example.cpp | 7 ++++ 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/src/cxxopts.hpp b/src/cxxopts.hpp index f3908c6..194e384 100644 --- a/src/cxxopts.hpp +++ b/src/cxxopts.hpp @@ -43,8 +43,14 @@ namespace cxxopts virtual void parse(const std::string& text) const = 0; + virtual void + parse() const = 0; + virtual bool has_arg() const = 0; + + virtual bool + has_default() const = 0; }; class OptionException : public std::exception @@ -237,12 +243,24 @@ namespace cxxopts parse_value(text, *m_store); } + void + parse() const + { + parse_value("", *m_store); + } + bool has_arg() const { return value_has_arg::value; } + bool + has_default() const + { + return false; + } + const T& get() const { @@ -256,11 +274,50 @@ namespace cxxopts } } - private: + protected: std::shared_ptr m_result; T* m_store; }; + template + class default_value_default : public default_value + { + using base_type = default_value; + + public: + default_value_default(T value) + : base_type(), m_default(value) + { + } + + void + parse(const std::string& text) const + { + parse_value(text, *base_type::m_store); + } + + void + parse() const + { + *base_type::m_store = m_default; + } + + bool + has_arg() const + { + return value_has_arg::value; + } + + bool + has_default() const + { + return true; + } + + private: + T m_default; + }; + } template @@ -277,6 +334,13 @@ namespace cxxopts return std::make_shared>(&t); } + template + std::shared_ptr + value_default(const T& t) + { + return std::make_shared>(t); + } + class OptionAdder; class OptionDetails @@ -312,12 +376,23 @@ namespace cxxopts ++m_count; } + void + parse_default() + { + m_value->parse(); + ++m_count; + } + int count() const { return m_count; } + const Value& value() const { + return *m_value; + } + template const T& as() const @@ -799,6 +874,16 @@ Options::parse(int& argc, char**& argv) ++current; } + for (auto& opt : m_options) + { + auto& detail = opt.second; + auto& value = detail->value(); + + if(!detail->count() && value.has_default()){ + detail->parse_default(); + } + } + argc = nextKeep; } diff --git a/src/example.cpp b/src/example.cpp index 989d40c..59e9983 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -38,6 +38,7 @@ int main(int argc, char* argv[]) ("a,apple", "an apple", cxxopts::value(apple)) ("b,bob", "Bob") ("f,file", "File", cxxopts::value>()) + ("o,output", "Output file", cxxopts::value_default("a.out")) ("positional", "Positional arguments: these are the arguments that are entered " "without an option", cxxopts::value()) @@ -88,6 +89,12 @@ int main(int argc, char* argv[]) << std::endl; } + if (options.count("output")) + { + std::cout << "Output = " << options["output"].as() + << std::endl; + } + if (options.count("int")) { std::cout << "int = " << options["int"].as() << std::endl; From 7e8f2e0b2626149f1ec7e414cb99293c60645b20 Mon Sep 17 00:00:00 2001 From: Baptiste Wicht Date: Sun, 26 Oct 2014 21:18:54 +0100 Subject: [PATCH 02/12] Complete the help --- src/cxxopts.hpp | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/src/cxxopts.hpp b/src/cxxopts.hpp index 194e384..0597b3c 100644 --- a/src/cxxopts.hpp +++ b/src/cxxopts.hpp @@ -51,6 +51,9 @@ namespace cxxopts virtual bool has_default() const = 0; + + virtual std::string + get_default_value() const = 0; }; class OptionException : public std::exception @@ -261,6 +264,12 @@ namespace cxxopts return false; } + std::string + get_default_value() const + { + return ""; + } + const T& get() const { @@ -314,6 +323,12 @@ namespace cxxopts return true; } + std::string + get_default_value() const + { + return m_default; + } + private: T m_default; }; @@ -412,6 +427,8 @@ namespace cxxopts std::string l; std::string desc; bool has_arg; + bool has_default; + std::string default_value; }; struct HelpGroupDetails @@ -580,11 +597,12 @@ namespace cxxopts std::string format_option ( - const std::string& s, - const std::string& l, - bool has_arg + const HelpOptionDetails& o ) { + auto& s = o.s; + auto& l = o.l; + std::string result = " "; if (s.size() > 0) @@ -601,9 +619,14 @@ namespace cxxopts result += " --" + l; } - if (has_arg) + if (o.has_arg) { result += " arg"; + + if (o.has_default) + { + result += " [" + o.default_value + "]"; + } } return result; @@ -911,7 +934,8 @@ Options::add_option //add the help details auto& options = m_help[group]; - options.options.emplace_back(HelpOptionDetails{s, l, desc, value->has_arg()}); + options.options.emplace_back(HelpOptionDetails{s, l, desc, + value->has_arg(), value->has_default(), value->get_default_value()}); } void @@ -953,7 +977,7 @@ Options::help_one_group(const std::string& g) const for (const auto& o : group->second.options) { - auto s = format_option(o.s, o.l, o.has_arg); + auto s = format_option(o); longest = std::max(longest, s.size()); format.push_back(std::make_pair(s, std::string())); } From b343ad98553999de1adba44b9ede3f92563ca7a1 Mon Sep 17 00:00:00 2001 From: Baptiste Wicht Date: Sun, 26 Oct 2014 21:31:16 +0100 Subject: [PATCH 03/12] Make the model more powerful --- src/cxxopts.hpp | 87 ++++++++++++++----------------------------------- src/example.cpp | 2 +- 2 files changed, 25 insertions(+), 64 deletions(-) diff --git a/src/cxxopts.hpp b/src/cxxopts.hpp index 0597b3c..59b9eaf 100644 --- a/src/cxxopts.hpp +++ b/src/cxxopts.hpp @@ -36,7 +36,7 @@ THE SOFTWARE. namespace cxxopts { - class Value + class Value : public std::enable_shared_from_this { public: @@ -54,6 +54,9 @@ namespace cxxopts virtual std::string get_default_value() const = 0; + + virtual std::shared_ptr + default_value(const std::string& value) = 0; }; class OptionException : public std::exception @@ -226,17 +229,19 @@ namespace cxxopts }; template - class default_value : public Value + class standard_value : public Value { public: - default_value() + standard_value() : m_result(std::make_shared()) , m_store(m_result.get()) + , m_default(false), m_default_value("") { } - default_value(T* t) + standard_value(T* t) : m_store(t) + , m_default(false), m_default_value("") { } @@ -249,7 +254,7 @@ namespace cxxopts void parse() const { - parse_value("", *m_store); + parse_value(m_default_value, *m_store); } bool @@ -261,13 +266,20 @@ namespace cxxopts bool has_default() const { - return false; + return m_default; + } + + virtual std::shared_ptr + default_value(const std::string& value){ + m_default = true; + m_default_value = value; + return shared_from_this(); } std::string get_default_value() const { - return ""; + return m_default_value; } const T& @@ -286,74 +298,23 @@ namespace cxxopts protected: std::shared_ptr m_result; T* m_store; + bool m_default; + std::string m_default_value; }; - - template - class default_value_default : public default_value - { - using base_type = default_value; - - public: - default_value_default(T value) - : base_type(), m_default(value) - { - } - - void - parse(const std::string& text) const - { - parse_value(text, *base_type::m_store); - } - - void - parse() const - { - *base_type::m_store = m_default; - } - - bool - has_arg() const - { - return value_has_arg::value; - } - - bool - has_default() const - { - return true; - } - - std::string - get_default_value() const - { - return m_default; - } - - private: - T m_default; - }; - } template std::shared_ptr value() { - return std::make_shared>(); + return std::make_shared>(); } template std::shared_ptr value(T& t) { - return std::make_shared>(&t); - } - - template - std::shared_ptr - value_default(const T& t) - { - return std::make_shared>(t); + return std::make_shared>(&t); } class OptionAdder; @@ -412,7 +373,7 @@ namespace cxxopts const T& as() const { - return dynamic_cast&>(*m_value).get(); + return dynamic_cast&>(*m_value).get(); } private: diff --git a/src/example.cpp b/src/example.cpp index 59e9983..6815f64 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -38,7 +38,7 @@ int main(int argc, char* argv[]) ("a,apple", "an apple", cxxopts::value(apple)) ("b,bob", "Bob") ("f,file", "File", cxxopts::value>()) - ("o,output", "Output file", cxxopts::value_default("a.out")) + ("o,output", "Output file", cxxopts::value()->default_value("a.out")) ("positional", "Positional arguments: these are the arguments that are entered " "without an option", cxxopts::value()) From 456ea951ab097c5efd555151c3c49698a8f51dd7 Mon Sep 17 00:00:00 2001 From: Baptiste Wicht Date: Sun, 26 Oct 2014 22:05:24 +0100 Subject: [PATCH 04/12] Add support for implicit values --- src/cxxopts.hpp | 70 ++++++++++++++++++++++++++++++++++++++++--------- src/example.cpp | 3 ++- 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/src/cxxopts.hpp b/src/cxxopts.hpp index 59b9eaf..bdf49a1 100644 --- a/src/cxxopts.hpp +++ b/src/cxxopts.hpp @@ -52,11 +52,17 @@ namespace cxxopts virtual bool has_default() const = 0; + virtual bool + has_implicit() const = 0; + virtual std::string get_default_value() const = 0; virtual std::shared_ptr default_value(const std::string& value) = 0; + + virtual std::shared_ptr + implicit_value(const std::string& value) = 0; }; class OptionException : public std::exception @@ -236,19 +242,28 @@ namespace cxxopts : m_result(std::make_shared()) , m_store(m_result.get()) , m_default(false), m_default_value("") + , m_implicit(false), m_implicit_value("") { } standard_value(T* t) : m_store(t) , m_default(false), m_default_value("") + , m_implicit(false), m_implicit_value("") { } void parse(const std::string& text) const { - parse_value(text, *m_store); + if (m_implicit && text.empty()) + { + parse_value(m_implicit_value, *m_store); + } + else + { + parse_value(text, *m_store); + } } void @@ -268,7 +283,13 @@ namespace cxxopts { return m_default; } - + + bool + has_implicit() const + { + return m_implicit; + } + virtual std::shared_ptr default_value(const std::string& value){ m_default = true; @@ -276,6 +297,13 @@ namespace cxxopts return shared_from_this(); } + virtual std::shared_ptr + implicit_value(const std::string& value){ + m_implicit = true; + m_implicit_value = value; + return shared_from_this(); + } + std::string get_default_value() const { @@ -300,6 +328,8 @@ namespace cxxopts T* m_store; bool m_default; std::string m_default_value; + bool m_implicit; + std::string m_implicit_value; }; } @@ -495,7 +525,7 @@ namespace cxxopts ( int argc, char* argv[], - int argPos, + int& current, std::shared_ptr value, const std::string& name ); @@ -696,17 +726,34 @@ Options::checked_parse_arg ( int argc, char* argv[], - int argPos, + int& current, std::shared_ptr value, const std::string& name ) { - if (argPos >= argc) + if (current + 1 >= argc) { - throw missing_argument_exception(name); + if (value->value().has_implicit()) + { + parse_option(value, name, ""); + } + else + { + throw missing_argument_exception(name); + } + } + else + { + if (argv[current + 1][0] == '-' && value->value().has_implicit()) + { + parse_option(value, name, ""); + } + else + { + parse_option(value, name, argv[current + 1]); + ++current; + } } - - parse_option(value, name, argv[argPos]); } void @@ -799,8 +846,7 @@ Options::parse(int& argc, char**& argv) //it must be the last argument if (i + 1 == s.size()) { - checked_parse_arg(argc, argv, current+1, value, name); - ++current; + checked_parse_arg(argc, argv, current, value, name); } else { @@ -841,9 +887,7 @@ Options::parse(int& argc, char**& argv) if (opt->has_arg()) { //parse the next argument - checked_parse_arg(argc, argv, current + 1, opt, name); - - ++current; + checked_parse_arg(argc, argv, current, opt, name); } else { diff --git a/src/example.cpp b/src/example.cpp index 6815f64..898ea79 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -38,7 +38,8 @@ int main(int argc, char* argv[]) ("a,apple", "an apple", cxxopts::value(apple)) ("b,bob", "Bob") ("f,file", "File", cxxopts::value>()) - ("o,output", "Output file", cxxopts::value()->default_value("a.out")) + ("o,output", "Output file", cxxopts::value() + ->default_value("a.out")->implicit_value("b.def")) ("positional", "Positional arguments: these are the arguments that are entered " "without an option", cxxopts::value()) From 65093e0432c2a97efb19c52092cc48615bac386f Mon Sep 17 00:00:00 2001 From: Baptiste Wicht Date: Mon, 27 Oct 2014 19:19:54 +0100 Subject: [PATCH 05/12] Cleanup --- src/cxxopts.hpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/cxxopts.hpp b/src/cxxopts.hpp index 0deb2ef..91d3aa5 100644 --- a/src/cxxopts.hpp +++ b/src/cxxopts.hpp @@ -419,15 +419,11 @@ namespace cxxopts standard_value() : m_result(std::make_shared()) , m_store(m_result.get()) - , m_default(false), m_default_value("") - , m_implicit(false), m_implicit_value("") { } standard_value(T* t) : m_store(t) - , m_default(false), m_default_value("") - , m_implicit(false), m_implicit_value("") { } @@ -504,9 +500,9 @@ namespace cxxopts protected: std::shared_ptr m_result; T* m_store; - bool m_default; + bool m_default = false; std::string m_default_value; - bool m_implicit; + bool m_implicit = false; std::string m_implicit_value; }; } From cc65ebb4fc5a882e210f33bc53d767a809946457 Mon Sep 17 00:00:00 2001 From: Baptiste Wicht Date: Mon, 27 Oct 2014 19:25:44 +0100 Subject: [PATCH 06/12] Fix warning --- CMakeLists.txt | 2 +- src/cxxopts.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 34e6bed..0c163ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,7 @@ if(CXXOPTS_USE_UNICODE_HELP) pkg_check_modules(ICU REQUIRED icu-uc) set(CXXOPTS_LINKER_LIBRARIES "${ICU_LIBRARIES}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCXXOPTS_USE_UNICODE") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -DCXXOPTS_USE_UNICODE") endif() diff --git a/src/cxxopts.hpp b/src/cxxopts.hpp index 91d3aa5..5bd7d5f 100644 --- a/src/cxxopts.hpp +++ b/src/cxxopts.hpp @@ -998,7 +998,7 @@ Options::parse(int& argc, char**& argv) { const std::string& s = result[4]; - for (int i = 0; i != s.size(); ++i) + for (std::size_t i = 0; i != s.size(); ++i) { std::string name(1, s[i]); auto iter = m_options.find(name); From f691449c0a4428400fa866bc5a4dbb828ea2cf84 Mon Sep 17 00:00:00 2001 From: Baptiste Wicht Date: Mon, 27 Oct 2014 19:29:08 +0100 Subject: [PATCH 07/12] Revert change --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c163ab..34e6bed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,7 @@ if(CXXOPTS_USE_UNICODE_HELP) pkg_check_modules(ICU REQUIRED icu-uc) set(CXXOPTS_LINKER_LIBRARIES "${ICU_LIBRARIES}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -DCXXOPTS_USE_UNICODE") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCXXOPTS_USE_UNICODE") endif() From 32be452302acaf4d93f98bbcc1a2a12e47d7f1ee Mon Sep 17 00:00:00 2001 From: Baptiste Wicht Date: Tue, 28 Oct 2014 21:27:29 +0100 Subject: [PATCH 08/12] Fix compilation --- src/cxxopts.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cxxopts.hpp b/src/cxxopts.hpp index 5bd7d5f..8562966 100644 --- a/src/cxxopts.hpp +++ b/src/cxxopts.hpp @@ -790,7 +790,7 @@ namespace cxxopts if (o.has_default) { - result += " [" + o.default_value + "]"; + result += " [" + toLocalString(o.default_value) + "]"; } } @@ -1159,7 +1159,6 @@ Options::help_one_group(const std::string& g) const for (const auto& o : group->second.options) { auto s = format_option(o); - longest = std::max(longest, s.size()); longest = std::max(longest, stringLength(s)); format.push_back(std::make_pair(s, String())); } From 9642317b081cf115c885906210d4d63ccf68d1f6 Mon Sep 17 00:00:00 2001 From: Baptiste Wicht Date: Tue, 28 Oct 2014 21:36:33 +0100 Subject: [PATCH 09/12] Fix implicit and short --- src/cxxopts.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cxxopts.hpp b/src/cxxopts.hpp index 8562966..ed93ffd 100644 --- a/src/cxxopts.hpp +++ b/src/cxxopts.hpp @@ -1022,6 +1022,10 @@ Options::parse(int& argc, char**& argv) { checked_parse_arg(argc, argv, current, value, name); } + else if (value->value().has_implicit()) + { + parse_option(value, name, ""); + } else { //error From e09bb9052a8fe2cb4e37bd8598373ed06a449116 Mon Sep 17 00:00:00 2001 From: Baptiste Wicht Date: Wed, 29 Oct 2014 16:59:57 +0100 Subject: [PATCH 10/12] Enhance example --- src/example.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/example.cpp b/src/example.cpp index 6566569..1f5c820 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -39,7 +39,7 @@ int main(int argc, char* argv[]) ("b,bob", "Bob") ("f,file", "File", cxxopts::value>(), "FILE") ("o,output", "Output file", cxxopts::value() - ->default_value("a.out")->implicit_value("b.def")) + ->default_value("a.out")->implicit_value("b.def"), "BIN") ("positional", "Positional arguments: these are the arguments that are entered " "without an option", cxxopts::value()) From 0639e086eaf7ee24d5350883e94fdf5a6d59e292 Mon Sep 17 00:00:00 2001 From: Baptiste Wicht Date: Wed, 29 Oct 2014 17:04:53 +0100 Subject: [PATCH 11/12] Finalize help --- src/cxxopts.hpp | 47 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/src/cxxopts.hpp b/src/cxxopts.hpp index 2e24feb..ad36d84 100644 --- a/src/cxxopts.hpp +++ b/src/cxxopts.hpp @@ -233,9 +233,12 @@ namespace cxxopts virtual bool has_implicit() const = 0; - virtual std::string + virtual std::string get_default_value() const = 0; + virtual std::string + get_implicit_value() const = 0; + virtual std::shared_ptr default_value(const std::string& value) = 0; @@ -484,6 +487,12 @@ namespace cxxopts return m_default_value; } + std::string + get_implicit_value() const + { + return m_implicit_value; + } + const T& get() const { @@ -594,6 +603,8 @@ namespace cxxopts bool has_arg; bool has_default; std::string default_value; + bool has_implicit; + std::string implicit_value; std::string arg_help; }; @@ -789,18 +800,15 @@ namespace cxxopts if (o.has_arg) { - if (o.arg_help.size() != 0) + auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg"; + + if (o.has_implicit) { - result += " " + toLocalString(o.arg_help); + result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]"; } else { - result += " arg"; - } - - if (o.has_default) - { - result += " [" + toLocalString(o.default_value) + "]"; + result += " " + arg; } } @@ -810,20 +818,27 @@ namespace cxxopts String format_description ( - const String& text, + const HelpOptionDetails& o, int start, int width ) { + auto desc = o.desc; + + if (o.has_default) + { + desc += toLocalString(" (default:" + o.default_value + ")"); + } + String result; - auto current = std::begin(text); + auto current = std::begin(desc); auto startLine = current; auto lastSpace = current; int size = 0; - while (current != std::end(text)) + while (current != std::end(desc)) { if (*current == ' ') { @@ -1132,8 +1147,10 @@ Options::add_option //add the help details auto& options = m_help[group]; - options.options.emplace_back(HelpOptionDetails{s, l, stringDesc, - value->has_arg(), value->has_default(), value->get_default_value(), + options.options.emplace_back(HelpOptionDetails{s, l, stringDesc, + value->has_arg(), + value->has_default(), value->get_default_value(), + value->has_implicit(), value->get_implicit_value(), std::move(arg_help)}); } @@ -1189,7 +1206,7 @@ Options::help_one_group(const std::string& g) const auto fiter = format.begin(); for (const auto& o : group->second.options) { - auto d = format_description(o.desc, longest + OPTION_DESC_GAP, allowed); + auto d = format_description(o, longest + OPTION_DESC_GAP, allowed); result += fiter->first; if (stringLength(fiter->first) > longest) From 4e4e91c37d9ff5ffa83731259def166de0db9adb Mon Sep 17 00:00:00 2001 From: Baptiste Wicht Date: Wed, 29 Oct 2014 17:35:42 +0100 Subject: [PATCH 12/12] Fix warnings --- src/cxxopts.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cxxopts.hpp b/src/cxxopts.hpp index ad36d84..fe77a6b 100644 --- a/src/cxxopts.hpp +++ b/src/cxxopts.hpp @@ -396,7 +396,7 @@ namespace cxxopts inline void - parse_value(const std::string& text, bool& value) + parse_value(const std::string& /*text*/, bool& value) { //TODO recognise on, off, yes, no, enable, disable //so that we can write --long=yes explicitly @@ -915,7 +915,7 @@ void Options::parse_option ( std::shared_ptr value, - const std::string& name, + const std::string& /*name*/, const std::string& arg ) {