Possibility to handle class method & example
[command.git] / include / option.h
index 935914e86112dca86c6e8bf5fad935785773c843..d9630a798b486bed5225e802d09c519e85c4ef4c 100644 (file)
@@ -6,6 +6,9 @@
 #include <stdexcept>
 
 #include "parameter.h"
+#include "exception/missingOptionValue.h"
+#include "exception/optionFailedConversion.h"
+#include "exception/optionValueNotSpecified.h"
 
 namespace command {
     /**
@@ -13,13 +16,13 @@ namespace command {
      * Options are named parameters of program.
      *
      * Example:
-     *  ./myprog OptionName=OptionValue
-     *  ./myprog -f=/some/file
-     *  ./myprog --level=15
+     *  ./myprog OptionName=OptionValue
+     *  ./myprog -f=/some/file
+     *  ./myprog --level=15
      */
-    template<typename OptionType>
+    template<typename ParameterType>
     class Option
-        : public Parameter, public Callable<OptionType>  {
+        : public Parameter, public Callable<ParameterType>  {
     public:
         typedef std::string OptionName;
     protected:
@@ -31,7 +34,7 @@ namespace command {
         /**
          * Current Option value
          */
-        OptionType value;
+        ParameterType value;
 
     public:
         /**
@@ -41,8 +44,8 @@ namespace command {
          * @param description Description of current Option
          * @param function Function used to handle current Option.
          */
-        Option(const std::string & name, const std::string & description, void (*function)(OptionType))
-            : Parameter(description), Callable<OptionType>(function), name(name) {
+        Option(const std::string & name, const std::string & description, std::function<void(ParameterType)> function)
+            : Parameter(description), Callable<ParameterType>(function), name(name) {
         }
 
         /**
@@ -51,10 +54,11 @@ namespace command {
         virtual ~Option() { }
 
         /**
-         *
+         * \inheritdoc
          */
         virtual void handle() {
             this->call(value);
+            used = true;
         }
 
         /**
@@ -68,7 +72,7 @@ namespace command {
          * If no equal sign is after OptionName part,
          * std::invalid_argument exception with appropriate message is thrown
          *
-         * If conversion of OptionValue part to OptionType failed,
+         * If conversion of OptionValue part to ParameterType failed,
          * std::invalid_argument exception with appropriate message is thrown
          *
          * @param argv command line value against which test will be made.
@@ -76,39 +80,52 @@ namespace command {
          *
          * @return If passed argv succesfully detected OptionName part as a
          *  current option and its OptionValue part has been succesfully
-         *  converted to OptionType, returns true and Option is set as used one.
+         *  converted to ParameterType, returns true and Option is set as used one.
          *  Otherwise returns false and can be used to check against next value.
          *
-         * @throw std::invalid_argument when OptionName part has no equal sign
-         *  after itself
-         * @throw std::invalid_argument when OptionValue part failed conversion
-         *  to OptionType
+         * @throw MissingOptionValue when OptionValue part is missing after
+         *  equal sign
+         * @throw OptionFailedConversion when OptionValue part failed conversion
+         *  to ParameterType
          */
-        virtual bool understand(const std::string & argv)
-            throw(std::invalid_argument) {
-
-            if ((!isUsed()) && (argv.find(name) == 0)) {
-                std::size_t pos = argv.find("=");
+        virtual bool understand(const std::string & argv) {
+            if (this->hasName(argv)) {
+                std::size_t pos = this->valuePosition(argv);
 
                 if (pos != name.size()) {
-                    throw std::invalid_argument("Option: " + name + " requires value but no one has been provided");
+                    throw MissingOptionValue("Option: " + name + " requires value but no one has been provided");
                 }
 
                 std::stringstream ss;
-                ss << argv.substr(pos + 1);
-                ss >> value;// memory leak? when uncommented and exception is
-                            // thrown, valgrind shows e.g.:
-                            //  possibly lost: 380 bytes in 7 blocks
+                ss << std::fixed << argv.substr(pos + 1);
+                ss >> value;
 
                 if (ss.fail()) {
-                    throw std::invalid_argument("Value for option: " + name + " failed conversion to the required type");
+                    throw OptionFailedConversion("Option: " + name + " failed value conversion to the required type");
                 }
 
-                used = true;
                 return true;
             }
             return false;
         }
+
+        /**
+         * \inheritdoc
+         */
+        virtual unsigned int valuePosition(const std::string & value) {
+            std::size_t pos = value.find("=");
+
+            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;
+        }
     };
 
     /**
@@ -141,7 +158,7 @@ namespace command {
          * @param description Description of current Option
          * @param function Function used to handle current Option.
          */
-        Option(const std::string & name, const std::string & description, void (*function)(void))
+        Option(const std::string & name, const std::string & description, std::function<void(void)> function)
             : Parameter(description), Callable<void>(function), name(name) {
         }
 
@@ -150,6 +167,7 @@ namespace command {
          */
         virtual void handle() {
             this->call();
+            used = true;
         }
 
         /**
@@ -168,13 +186,18 @@ 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;
         }
+
+        /**
+         * \inheritdoc
+         */
+        virtual unsigned int valuePosition(const std::string & ) {
+            throw new std::invalid_argument(this->describe() + " is void Option, so it does not have value part");
+        }
     };
 }