From 32ec13f77dce87c6f8086baa954b1d0f9fedb89c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Rafa=C5=82=20D=C5=82ugo=C5=82=C4=99cki?= Date: Tue, 12 May 2015 17:58:31 +0200 Subject: [PATCH] Add Possibility to set MultiValue Parameters. --- include/argument.h | 14 +-- include/command.h | 2 +- include/multiValue.h | 105 ++++++++++++++++++ include/option.h | 9 +- tests/Makefile.am | 5 +- .../should_extract_values_by_separator.cpp | 54 +++++++++ 6 files changed, 174 insertions(+), 15 deletions(-) create mode 100644 include/multiValue.h create mode 100644 tests/multiValue/should_extract_values_by_separator.cpp diff --git a/include/argument.h b/include/argument.h index 5690e5d..a32cf82 100644 --- a/include/argument.h +++ b/include/argument.h @@ -45,6 +45,7 @@ namespace command { */ virtual void handle() { this->call(value); + this->used = true; } /** @@ -64,16 +65,13 @@ namespace command { * against next value. */ virtual bool understand(const std::string & argv) { - if (!isUsed()) { - std::stringstream ss; + std::stringstream ss; - ss << argv; - ss >> value; + ss << argv; + ss >> value; - if (!ss.fail()) { - this->used = true; - return true; - } + if (!ss.fail()) { + return true; } return false; diff --git a/include/command.h b/include/command.h index 0cbb3d6..15d9a6b 100644 --- a/include/command.h +++ b/include/command.h @@ -53,7 +53,7 @@ namespace command { void matchArguments(unsigned int argc, char *argv[]) { for (unsigned int i = 1; i < argc; i++) { for(Parameter *param : parameters) { - if (param->understand(argv[i])) { + if (!param->isUsed() && param->understand(argv[i])) { param->handle(); break; } diff --git a/include/multiValue.h b/include/multiValue.h new file mode 100644 index 0000000..e84b855 --- /dev/null +++ b/include/multiValue.h @@ -0,0 +1,105 @@ +#ifndef __COMMAND_MULTIVALUE_H +#define __COMMAND_MULTIVALUE_H + +#include + +#include "parameter.h" + +namespace command { + /** + * Multiple Value Parameter decorator. Allows Parameters to understand + * many values. + * + * Example: + * - separator: "," + * - multiValue: "0,1,2,3,4,5,6,7,8,9" + * + * Example usage: + * - ./myprog ARGUMENT,ARGUMENT,ARGUMENT,ARGUMENT + * - ./myprog OPTION_NAME=VALUE,VALUE,VALUE,VALUE + */ + class MultiValue : public Parameter { + protected: + /** + * Parameter which will be treated as containing multiple values + */ + Parameter * parameter; + + std::vector values; + + std::string separator; + + public: + /** + * Default constructor. + * + * @param parameter Parameter which will be treated as containing multiple values + */ + MultiValue(std::string separator, Parameter * parameter) + : Parameter(parameter->describe()), parameter(parameter), + separator(separator) { + } + + /** + * Default destructor. Releases allocated memory + */ + virtual ~MultiValue() { + delete parameter; + } + + /** + * Wrapper method around passed Parameter::handle(). + * + * \inheritdoc + */ + virtual void handle() { + for (std::string value : values) { + parameter->understand(value); + parameter->handle(); + } + } + + /** + * 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) { + size_t start = 0; + size_t pos = 0; + bool _understand = true; + + do { + pos = value.find(separator, start); + values.push_back(value.substr(start, pos-start)); + _understand &= parameter->understand(values.back()); + start = pos + 1; + } while ((pos != std::string::npos) && (start < value.size())); + + return _understand; + } + + /** + * Wrapper method around passed Parameter::isRequired(). + * Indicates if current Parameter is required. + * + * @return true if Parameter is required, false otherwise + */ + virtual bool isRequired() { + return parameter->isRequired(); + }; + + /** + * Wrapper method around passed Parameter::isUsed(). + * + * \inheritdoc + */ + virtual bool isUsed() { + return parameter->isUsed(); + }; + }; +} + +#endif /* __COMMAND_PARAMETER_H */ diff --git a/include/option.h b/include/option.h index 21f814e..59de395 100644 --- a/include/option.h +++ b/include/option.h @@ -57,6 +57,7 @@ namespace command { */ virtual void handle() { this->call(value); + used = true; } /** @@ -88,7 +89,7 @@ namespace command { */ virtual bool understand(const std::string & argv) { - if ((!isUsed()) && (argv.find(name) == 0)) { + if (argv.find(name) == 0) { std::size_t pos = argv.find("="); if (pos != name.size()) { @@ -103,7 +104,6 @@ namespace command { throw OptionFailedConversion("Value for option: " + name + " failed conversion to the required type"); } - used = true; return true; } return false; @@ -149,6 +149,7 @@ namespace command { */ virtual void handle() { this->call(); + used = true; } /** @@ -167,9 +168,7 @@ namespace command { * used to check against next value. */ virtual bool understand(const std::string & argv) { - if ((!isUsed()) && - (argv == name)) { - used = true; + if (argv == name) { return true; } return false; diff --git a/tests/Makefile.am b/tests/Makefile.am index f338994..d7b072a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -21,7 +21,8 @@ TESTS = \ option_handles_void_value.test \ option_should_match_exact_name.test \ option_should_throw_exception_on_missing_value.test \ - required_should_be_required.test + required_should_be_required.test \ + multivalue_should_extract_values_by_separator.test noinst_PROGRAMS = $(TESTS) @@ -53,3 +54,5 @@ option_should_match_exact_name_test_SOURCES = option/should_match_exact_name.cp option_should_throw_exception_on_missing_value_test_SOURCES = option/should_throw_exception_on_missing_value.cpp required_should_be_required_test_SOURCES = required/should_be_required.cpp + +multivalue_should_extract_values_by_separator_test_SOURCES = multiValue/should_extract_values_by_separator.cpp diff --git a/tests/multiValue/should_extract_values_by_separator.cpp b/tests/multiValue/should_extract_values_by_separator.cpp new file mode 100644 index 0000000..8b5e5e3 --- /dev/null +++ b/tests/multiValue/should_extract_values_by_separator.cpp @@ -0,0 +1,54 @@ +#include +#include + +#include "argument.h" +#include "multiValue.h" + +using namespace std; +using namespace command; + +#define VALUE "0,1,2,3,4,5,6,7,8,9" + +typedef int ArgumentType; + +std::vector input; + +void function(ArgumentType value) { + input.push_back(value); + cout << "Catched value: " << value << "\n"; +} + +int main() { + Parameter * argument = new MultiValue(",", new Argument("Argument as multiValue int", function)); + + try { + if (argument->understand(VALUE)) { + argument->handle(); + } + else { + cout << argument->describe() << " should understand multiple int values\n"; + return 1; + } + } + catch (...) { + delete argument; + cout << argument->describe() << " thrown unknown exception\n"; + return 1; + } + + bool test = true; + for (int i = 0; i < 10; i++) { + test &= (input[i] == i); + cout << i << ") input: " << input[i] << "\n"; + } + + if (test) { + cout << argument->describe() << " handles boolean (TRUE) values\n"; + delete argument; + return 0; + } + + cout << argument->describe() << " do not handle multiple int values\n"; + delete argument; + return 1; +} -- 2.30.2