From d625e80d62fc8b98c18a95c65e6fa329b7fcc85b Mon Sep 17 00:00:00 2001 From: Maxim Mamontov Date: Sat, 9 Nov 2013 22:57:17 +0200 Subject: [PATCH 1/1] Implemented command line options parsing. --- projects/sgconf/action.h | 8 +++ projects/sgconf/actions.cpp | 11 ---- projects/sgconf/actions.h | 22 +++++--- projects/sgconf/main.cpp | 103 ++++++++++++++++++++++++++++++------ projects/sgconf/options.cpp | 48 +++++++++++++---- projects/sgconf/options.h | 31 ++++++++--- 6 files changed, 174 insertions(+), 49 deletions(-) diff --git a/projects/sgconf/action.h b/projects/sgconf/action.h index 469d7460..64d7c1e8 100644 --- a/projects/sgconf/action.h +++ b/projects/sgconf/action.h @@ -35,6 +35,7 @@ class ACTION public: virtual ~ACTION() {} + virtual ACTION * Clone() const = 0; virtual std::string ParamDescription() const = 0; virtual std::string DefaultDescription() const = 0; virtual OPTION_BLOCK & Suboptions() = 0; @@ -48,6 +49,13 @@ class ACTION }; }; +template +class ACTION_CLONE_MIXIN : public ACTION +{ + public: + virtual ACTION * Clone() const { return new T(*this); } +}; + } // namespace SGCONF #endif diff --git a/projects/sgconf/actions.cpp b/projects/sgconf/actions.cpp index 742ff042..afa51621 100644 --- a/projects/sgconf/actions.cpp +++ b/projects/sgconf/actions.cpp @@ -17,14 +17,3 @@ /* * Author : Maxim Mamontov */ - -#include "actions.h" - -using SGCONF::FUNC0_ACTION; -using SGCONF::PARSER_STATE; - -PARSER_STATE FUNC0_ACTION::Parse(int argc, char ** argv) -{ -m_func(); -return PARSER_STATE(true, argc, argv); -} diff --git a/projects/sgconf/actions.h b/projects/sgconf/actions.h index 739fbf14..886e17e2 100644 --- a/projects/sgconf/actions.h +++ b/projects/sgconf/actions.h @@ -35,29 +35,37 @@ namespace SGCONF typedef void (* FUNC0)(); +template class FUNC0_ACTION : public ACTION { public: - FUNC0_ACTION(FUNC0 func) : m_func(func) {} + FUNC0_ACTION(const F & func) : m_func(func) {} + + virtual ACTION * Clone() const { return new FUNC0_ACTION(*this); } virtual std::string ParamDescription() const { return ""; } virtual std::string DefaultDescription() const { return ""; } virtual OPTION_BLOCK & Suboptions() { return m_suboptions; } - virtual PARSER_STATE Parse(int argc, char ** argv); + virtual PARSER_STATE Parse(int argc, char ** argv) + { + m_func(); + return PARSER_STATE(true, argc, argv); + } private: - FUNC0 m_func; + F m_func; OPTION_BLOCK m_suboptions; }; +template inline -FUNC0_ACTION * MakeFunc0Action(FUNC0 func) +FUNC0_ACTION * MakeFunc0Action(F func) { -return new FUNC0_ACTION(func); +return new FUNC0_ACTION(func); } template -class PARAM_ACTION: public ACTION +class PARAM_ACTION : public ACTION { public: PARAM_ACTION(RESETABLE & param, @@ -75,6 +83,8 @@ class PARAM_ACTION: public ACTION m_hasDefault(false) {} + virtual ACTION * Clone() const { return new PARAM_ACTION(*this); } + virtual std::string ParamDescription() const { return m_description; } virtual std::string DefaultDescription() const; virtual OPTION_BLOCK & Suboptions() { return m_suboptions; } diff --git a/projects/sgconf/main.cpp b/projects/sgconf/main.cpp index 8644e690..a1c8df8f 100644 --- a/projects/sgconf/main.cpp +++ b/projects/sgconf/main.cpp @@ -65,6 +65,65 @@ struct ARRAY_TYPE typedef T type; }; +template +struct nullary_function +{ +typedef T result_type; +}; + +template +class binder0 : public nullary_function +{ + public: + binder0(const F & func, const typename F::argument_type & arg) + : m_func(func), m_arg(arg) {} + typename F::result_type operator()() const { return m_func(m_arg); } + private: + F m_func; + typename F::argument_type m_arg; +}; + +template +inline +binder0 bind0(const F & func, const typename F::argument_type & arg) +{ +return binder0(func, arg); +} + +template +class METHOD1_ADAPTER : public std::unary_function +{ + public: + METHOD1_ADAPTER(R (C::* func)(A), C & obj) : m_func(func), m_obj(obj) {} + R operator()(A arg) { return (m_obj.*m_func)(arg); } + private: + R (C::* m_func)(A); + C & m_obj; +}; + +template +class CONST_METHOD1_ADAPTER : public std::unary_function +{ + public: + CONST_METHOD1_ADAPTER(R (C::* func)(A) const, C & obj) : m_func(func), m_obj(obj) {} + R operator()(A arg) const { return (m_obj.*m_func)(arg); } + private: + R (C::* m_func)(A) const; + C & m_obj; +}; + +template +METHOD1_ADAPTER Method1Adapt(R (C::* func)(A), C & obj) +{ +return METHOD1_ADAPTER(func, obj); +} + +template +CONST_METHOD1_ADAPTER Method1Adapt(R (C::* func)(A) const, C & obj) +{ +return CONST_METHOD1_ADAPTER(func, obj); +} + template bool SetArrayItem(T & array, const char * index, const typename ARRAY_TYPE::type & value) { @@ -92,7 +151,7 @@ void Version(); namespace SGCONF { -class CONFIG_ACTION: public ACTION +class CONFIG_ACTION : public ACTION { public: CONFIG_ACTION(CONFIG & config, @@ -101,6 +160,8 @@ class CONFIG_ACTION: public ACTION m_description(paramDescription) {} + virtual ACTION * Clone() const { return new CONFIG_ACTION(*this); } + virtual std::string ParamDescription() const { return m_description; } virtual std::string DefaultDescription() const { return ""; } virtual OPTION_BLOCK & Suboptions() { return m_suboptions; } @@ -1138,23 +1199,33 @@ return ProcessSetUser(req.server.data(), req.port.data(), req.admLogin.data(), r //----------------------------------------------------------------------------- int main(int argc, char **argv) { -UsageAll(); -exit(0); - SGCONF::CONFIG config; -SGCONF::OPTION_BLOCK generalOptions; -generalOptions.Add("c", "config", SGCONF::MakeParamAction(config.configFile, std::string("~/.config/stg/sgconf.conf"), ""), "override default config file"); -generalOptions.Add("h", "help", SGCONF::MakeFunc0Action(Usage), "show this help and exit"); -generalOptions.Add("help-all", SGCONF::MakeFunc0Action(UsageAll), "show full help and exit"); -generalOptions.Add("v", "version", SGCONF::MakeFunc0Action(Version), "show version information and exit"); - -SGCONF::OPTION_BLOCK connOptions; -connOptions.Add("s", "server", SGCONF::MakeParamAction(config.server, std::string("localhost"), "
"), "host to connect"); -connOptions.Add("p", "port", SGCONF::MakeParamAction(config.port, uint16_t(5555), ""), "port to connect"); -connOptions.Add("u", "username", SGCONF::MakeParamAction(config.userName, std::string("admin"), ""), "administrative login"); -connOptions.Add("w", "userpass", SGCONF::MakeParamAction(config.userPass, ""), "password for the administrative login"); -connOptions.Add("a", "address", SGCONF::MakeParamAction(config, ""), "connection params as a single string in format: :@:"); +SGCONF::OPTION_BLOCKS blocks; +blocks.Add("General options") + .Add("c", "config", SGCONF::MakeParamAction(config.configFile, std::string("~/.config/stg/sgconf.conf"), ""), "override default config file") + .Add("h", "help", SGCONF::MakeFunc0Action(bind0(Method1Adapt(&SGCONF::OPTION_BLOCKS::Help, blocks), 0)), "\t\tshow this help and exit") + .Add("help-all", SGCONF::MakeFunc0Action(UsageAll), "\t\tshow full help and exit") + .Add("v", "version", SGCONF::MakeFunc0Action(Version), "\t\tshow version information and exit"); +blocks.Add("Connection options") + .Add("s", "server", SGCONF::MakeParamAction(config.server, std::string("localhost"), "
"), "\t\thost to connect") + .Add("p", "port", SGCONF::MakeParamAction(config.port, uint16_t(5555), ""), "\t\tport to connect") + .Add("u", "username", SGCONF::MakeParamAction(config.userName, std::string("admin"), ""), "\tadministrative login") + .Add("w", "userpass", SGCONF::MakeParamAction(config.userPass, ""), "\tpassword for the administrative login") + .Add("a", "address", SGCONF::MakeParamAction(config, ""), "connection params as a single string in format: :@:"); + +SGCONF::PARSER_STATE state(blocks.Parse(--argc, ++argv)); // Skipping self name + +if (state.stop) + return 0; + +if (state.argc > 0) + { + std::cerr << "Unknown option: '" << *state.argv << "'\n"; + return -1; + } + +return 0; if (argc < 2) { diff --git a/projects/sgconf/options.cpp b/projects/sgconf/options.cpp index 71f9687c..06889174 100644 --- a/projects/sgconf/options.cpp +++ b/projects/sgconf/options.cpp @@ -29,6 +29,7 @@ using SGCONF::OPTION; using SGCONF::OPTION_BLOCK; +using SGCONF::OPTION_BLOCKS; using SGCONF::ACTION; using SGCONF::PARSER_STATE; @@ -55,7 +56,7 @@ OPTION::OPTION(const std::string & longName, OPTION::OPTION(const OPTION & rhs) : m_shortName(rhs.m_shortName), m_longName(rhs.m_longName), - m_action(rhs.m_action), + m_action(rhs.m_action->Clone()), m_description(rhs.m_description) { } @@ -106,26 +107,31 @@ catch (const ACTION::ERROR & ex) } } -void OPTION_BLOCK::Add(const std::string & shortName, - const std::string & longName, - ACTION * action, - const std::string & description) +OPTION_BLOCK & OPTION_BLOCK::Add(const std::string & shortName, + const std::string & longName, + ACTION * action, + const std::string & description) { m_options.push_back(OPTION(shortName, longName, action, description)); +return *this; } -void OPTION_BLOCK::Add(const std::string & longName, - ACTION * action, - const std::string & description) +OPTION_BLOCK & OPTION_BLOCK::Add(const std::string & longName, + ACTION * action, + const std::string & description) { m_options.push_back(OPTION(longName, action, description)); +return *this; } void OPTION_BLOCK::Help(size_t level) const { +if (m_options.empty()) + return; +std::cout << m_description << ":\n"; std::for_each(m_options.begin(), m_options.end(), - std::bind2nd(std::mem_fun_ref(&OPTION::Help), level)); + std::bind2nd(std::mem_fun_ref(&OPTION::Help), level + 1)); } PARSER_STATE OPTION_BLOCK::Parse(int argc, char ** argv) @@ -138,6 +144,30 @@ while (state.argc > 0 && !state.stop) state = it->Parse(--state.argc, ++state.argv); else break; + ++it; + } +return state; +} + +void OPTION_BLOCKS::Help(size_t level) const +{ +std::list::const_iterator it(m_blocks.begin()); +while (it != m_blocks.end()) + { + it->Help(level); + std::cout << "\n"; + ++it; + } +} + +PARSER_STATE OPTION_BLOCKS::Parse(int argc, char ** argv) +{ +PARSER_STATE state(false, argc, argv); +std::list::iterator it(m_blocks.begin()); +while (!state.stop && it != m_blocks.end()) + { + state = it->Parse(state.argc, state.argv); + ++it; } return state; } diff --git a/projects/sgconf/options.h b/projects/sgconf/options.h index 696255ff..3d4bdd64 100644 --- a/projects/sgconf/options.h +++ b/projects/sgconf/options.h @@ -23,6 +23,7 @@ #include #include +#include #include #include // size_t @@ -68,13 +69,16 @@ class OPTION class OPTION_BLOCK { public: - void Add(const std::string & shortName, - const std::string & longName, - ACTION * action, - const std::string & description); - void Add(const std::string & longName, - ACTION * action, - const std::string & description); + OPTION_BLOCK() {} + OPTION_BLOCK(const std::string & description) + : m_description(description) {} + OPTION_BLOCK & Add(const std::string & shortName, + const std::string & longName, + ACTION * action, + const std::string & description); + OPTION_BLOCK & Add(const std::string & longName, + ACTION * action, + const std::string & description); void Help(size_t level) const; @@ -82,6 +86,19 @@ class OPTION_BLOCK private: std::vector