From f2d683504e2dc76868ec84d7c1fd858edc0df5a4 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Rafa=C5=82=20D=C5=82ugo=C5=82=C4=99cki?= Date: Thu, 14 May 2015 23:58:15 +0200 Subject: [PATCH] Add Grouped behaviour. --- include/argument.h | 2 +- include/command.h | 51 +++----------- include/grouped.h | 159 +++++++++++++++++++++++++++++++++++++++++++ include/multiValue.h | 16 ++++- include/option.h | 16 +++-- include/parameter.h | 7 ++ include/required.h | 2 +- src/main.cpp | 9 +-- 8 files changed, 206 insertions(+), 56 deletions(-) create mode 100644 include/grouped.h diff --git a/include/argument.h b/include/argument.h index 80deabb..81a7cd4 100644 --- a/include/argument.h +++ b/include/argument.h @@ -67,7 +67,7 @@ namespace command { virtual bool understand(const std::string & argv) { std::stringstream ss; - ss << argv; + ss << std::fixed << argv; ss >> value; if (!ss.fail()) { diff --git a/include/command.h b/include/command.h index 15d9a6b..7d01cb8 100644 --- a/include/command.h +++ b/include/command.h @@ -4,31 +4,32 @@ #include #include #include +#include #include "parameter.h" -#include "exception/missingRequiredParameter.h" +#include "grouped.h" namespace command { /** * Main class for handling user passed parameters from command line. */ - class Command { - protected: - std::vector parameters; + class Command : protected Grouped { public: /** * Default constructor. * - * @param argc passed to the main function - * @param argv passed to the main function + * @param argc from the main function + * @param argv from the main function * @param params initializer_list containing Parameter handlers * responsible for correctly handle user data. */ Command(unsigned int argc, char *argv[], std::initializer_list params) - : parameters(params) { - + : Grouped(params, "Command") { try { - matchArguments(argc, argv); + for (unsigned int i = 1; i < argc; i++) { + this->understand(argv[i]); + } + handle(); } catch(const std::invalid_argument & exception) { releaseMemory(); @@ -46,38 +47,6 @@ namespace command { ~Command() { releaseMemory(); } - protected: - /** - * Matches user passed arguments with available parameter handlers. - */ - void matchArguments(unsigned int argc, char *argv[]) { - for (unsigned int i = 1; i < argc; i++) { - for(Parameter *param : parameters) { - if (!param->isUsed() && param->understand(argv[i])) { - param->handle(); - break; - } - } - } - for(Parameter *param : parameters) { - if (param->isRequired() && !param->isUsed()) { - throw MissingRequiredParameter(param->describe() + " is required but it was not passed"); - } - } - } - - /** - * Releases acquired memory - */ - void releaseMemory() { - for (Parameter * parameter : parameters) { - if (parameter != NULL) { - delete parameter; - } - } - parameters.clear(); - parameters.shrink_to_fit(); - } }; } diff --git a/include/grouped.h b/include/grouped.h new file mode 100644 index 0000000..613943f --- /dev/null +++ b/include/grouped.h @@ -0,0 +1,159 @@ +#ifndef __COMMAND_GROUPED_H +#define __COMMAND_GROUPED_H + +#include +#include +#include + +#include "parameter.h" +#include "exception/missingRequiredParameter.h" + +namespace command { + /** + * Grouped Parameters decorator. Allows Parameters to understand be grouped + * together. + * + * Example usage: + * - ./myprog [ARGUMENT OPTION] [ARGUMENT OPTION] ... + */ + class Grouped : public Parameter { + protected: + /** + * Parameters which will be treated as grouped together + */ + std::vector parameters; + + std::vector values; + + std::vector understood; + std::vector all_used; + + public: + /** + * Default constructor. + * + * @param params Parameters which will be treated as grouped together + */ + Grouped(std::initializer_list params, std::string description = "Grouped") + : Parameter(description), parameters(params) { + for (unsigned int i = 0; i < parameters.size(); i++) { + understood.push_back(false); + values.push_back(""); + } + } + + /** + * Default destructor. Releases allocated memory + */ + virtual ~Grouped() { + releaseMemory(); + } + + /** + * Wrapper method around passed Parameter::handle(). + * + * \inheritdoc + */ + virtual void handle() { +// std::cout << this->describe() << " handles" << "\n"; +// if (!isUsed()) { + for (unsigned int i = 0; i < parameters.size(); i++) { + Parameter *param = parameters[i]; + if (understood[i] || param->hungryForValue()) { +// std::cout << param->describe() << "=" << values[i] << "\n"; + param->handle(); + } + } +// } + // @TODO: Nested required + for(Parameter *param : parameters) { + if (param->isRequired() && !param->isUsed()) { + throw MissingRequiredParameter(param->describe() + " is required"); + } + } + } + + /** + * Wrapper method around passed Parameter::understand() + * + * @param argv command line value against which test will be made + * + * \inheritdoc + */ + virtual bool understand(const std::string & value) { +// std::cout << "[+] Grouped [" << this->describe() << "]\n"; + + bool _understand = false; + +// std::cout << "GMD\n" ; + for (unsigned int i = 0; i < parameters.size(); i++) { + Parameter *param = parameters[i]; + +// std::cout << " > Grouped:Param [" << param->describe() << ", " << value << "]\n"; + if (!understood[i]) { + _understand = param->understand(value); + if (_understand) { +// std::cout << " > Understood [" << param->describe() << ", " << value << "]\n"; + understood[i] = _understand; + values[i] = value; + break; + } + } + } + + bool all_understood = (understood.size() == parameters.size()); + for (bool u : understood) { + all_understood &= u; + } + +// std::cout << this->describe() << " understand: " << std::boolalpha << all_understood << "\n"; + + return all_understood; + } + + /** + * Indicates if current Parameter has been already used + * + * @return true if current Parameter has been already used. False otherwise. + */ +// virtual bool isUsed() { +// if (!used) { +// // bool used +// for(Parameter *param : parameters) { +// param->isUsed(); +// } +// } +// } + + /** + * \inheritdoc + */ + virtual bool hungryForValue() { + return true; + } + + /** + * Wrapper method around passed Parameter::valuePosition(). + * + * \inheritdoc + */ + virtual unsigned int valuePosition(const std::string &) { + throw std::logic_error("Group does not have value part"); + } + + /** + * Releases acquired memory + */ + void releaseMemory() { + for (Parameter * parameter : parameters) { + if (parameter != NULL) { + delete parameter; + } + } + parameters.clear(); + parameters.shrink_to_fit(); + } + }; +} + +#endif /* __COMMAND_GROUPED_H */ diff --git a/include/multiValue.h b/include/multiValue.h index 62fbf4d..26738b5 100644 --- a/include/multiValue.h +++ b/include/multiValue.h @@ -68,11 +68,17 @@ namespace command { * \inheritdoc */ virtual bool understand(const std::string & value) { - size_t start = parameter->valuePosition(value); + size_t start = 0; size_t pos = 0; bool _understand = true; std::string prefix = ""; + start = parameter->valuePosition(value); + + if (start > value.size()) { + return false; + } + if (start > 0) { prefix = value.substr(0, ++start);// always count: "=" } @@ -82,8 +88,12 @@ namespace command { values.push_back(prefix + value.substr(start, pos-start)); _understand &= parameter->understand(values.back()); start = pos + 1; - } while ((pos != std::string::npos) && (start < value.size())); + if (!_understand) { + values.clear(); + break; + } + } while ((pos != std::string::npos) && (start < value.size())); return _understand; } @@ -117,4 +127,4 @@ namespace command { }; } -#endif /* __COMMAND_PARAMETER_H */ +#endif /* __COMMAND_MULTIVALUE_H */ diff --git a/include/option.h b/include/option.h index d66164d..7e9917b 100644 --- a/include/option.h +++ b/include/option.h @@ -89,8 +89,7 @@ namespace command { * to ParameterType */ virtual bool understand(const std::string & argv) { - - if (argv.find(name) == 0) { + if (this->hasName(argv)) { std::size_t pos = this->valuePosition(argv); if (pos != name.size()) { @@ -98,11 +97,11 @@ namespace command { } std::stringstream ss; - ss << argv.substr(pos + 1); + ss << std::fixed << argv.substr(pos + 1); ss >> value; if (ss.fail()) { - throw OptionFailedConversion("Value for option: " + name + " failed conversion to the required type"); + throw OptionFailedConversion("Option: " + name + " failed value conversion to the required type"); } return true; @@ -116,12 +115,17 @@ namespace command { virtual unsigned int valuePosition(const std::string & value) { std::size_t pos = value.find("="); - if (pos == std::string::npos) { - throw OptionValueNotSpecified("Option: " + name + " requires value to be specified using equal sign"); + if ((this->hasName(value)) && (pos == std::string::npos)) { + throw OptionValueNotSpecified("Option: " + name + " requires value to be specified after equal sign, but no equal sign was found"); } return pos; } + + protected: + bool hasName(const std::string & argv) { + return argv.find(name) == 0; + } }; /** diff --git a/include/parameter.h b/include/parameter.h index 1bbfa88..87c1590 100644 --- a/include/parameter.h +++ b/include/parameter.h @@ -2,6 +2,7 @@ #define __COMMAND_PARAMETER_H #include +#include #include "descriptive.h" #include "callable.h" @@ -64,6 +65,12 @@ namespace command { return used; } + /** + */ + virtual bool hungryForValue() { + return false; + } + /** * @return position where value starts in passed string */ diff --git a/include/required.h b/include/required.h index 726f6ac..f88d2ae 100644 --- a/include/required.h +++ b/include/required.h @@ -82,4 +82,4 @@ namespace command { }; } -#endif /* __COMMAND_PARAMETER_H */ +#endif /* __COMMAND_REQUIRED_H */ diff --git a/src/main.cpp b/src/main.cpp index 06fbf1e..5ad96a2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,6 +5,7 @@ #include "argument.h" #include "required.h" #include "multiValue.h" +#include "grouped.h" #include "command.h" using namespace command; @@ -24,12 +25,12 @@ void void_function(void) { int main(int argc, char *argv[]) { try { Command command(argc, argv, { -// new Argument("File path", [](std::string value)->void { std::cout << "Hello from lambda " << value << std::endl; }), - new Required(new MultiValue("-", new Argument("Input values", argument_function))), - new MultiValue(",", new Option("f", "Optional file", option_function)), + new Grouped({ + new Required(new MultiValue("-", new Argument("Input values", argument_function))), + new MultiValue(",", new Option("f", "Optional file", option_function)) + }), new Option("h", "Help", void_function) }); - } catch(const std::exception & e) { std::cout << e.what() << std::endl; -- 2.30.2