CommandLineParser: add special <none> value to disallow empty strings
authorPavel Rojtberg <pavel.rojtberg@igd.fraunhofer.de>
Wed, 23 Sep 2015 10:03:44 +0000 (12:03 +0200)
committerPavel Rojtberg <pavel.rojtberg@igd.fraunhofer.de>
Wed, 23 Sep 2015 13:43:46 +0000 (15:43 +0200)
some mandatory string keys like paths must not be empty. Add the special
default value `<none>` so the CommandLineParser can enforce this and
generate an according error message for us.

modules/core/include/opencv2/core/utility.hpp
modules/core/src/command_line_parser.cpp
modules/core/test/test_utils.cpp

index 22011dd..4d7d7df 100644 (file)
@@ -609,7 +609,7 @@ For example:
     const String keys =
         "{help h usage ? |      | print this message   }"
         "{@image1        |      | image1 for compare   }"
-        "{@image2        |      | image2 for compare   }"
+        "{@image2        |<none>| image2 for compare   }"
         "{@repeat        |1     | number               }"
         "{path           |.     | path to file         }"
         "{fps            | -1.0 | fps for output video }"
@@ -623,6 +623,9 @@ Note that there are no default values for `help` and `timestamp` so we can check
 Arguments with default values are considered to be always present. Use the `get()` method in these cases to check their
 actual value instead.
 
+String keys like `get<String>("@image1")` return the empty string `""` by default - even with an empty default value.
+Use the special `<none>` default value to enforce that the returned string must not be empty. (like in `get<String>("@image2")`)
+
 ### Usage
 
 For the described keys:
index 4ef4f7d..658c3c7 100644 (file)
@@ -4,6 +4,20 @@
 namespace cv
 {
 
+namespace {
+static const char* noneValue = "<none>";
+
+static String cat_string(const String& str)
+{
+    int left = 0, right = (int)str.length();
+    while( left <= right && str[left] == ' ' )
+        left++;
+    while( right > left && str[right-1] == ' ' )
+        right--;
+    return left >= right ? String("") : str.substr(left, right-left);
+}
+}
+
 struct CommandLineParserParams
 {
 public:
@@ -27,7 +41,6 @@ struct CommandLineParser::Impl
 
     std::vector<String> split_range_string(const String& str, char fs, char ss) const;
     std::vector<String> split_string(const String& str, char symbol = ' ', bool create_empty_item = false) const;
-    String cat_string(const String& str) const;
 
     void apply_params(const String& key, const String& value);
     void apply_params(int i, String value);
@@ -98,10 +111,10 @@ void CommandLineParser::getByName(const String& name, bool space_delete, int typ
                 {
                     String v = impl->data[i].def_value;
                     if (space_delete)
-                        v = impl->cat_string(v);
+                        v = cat_string(v);
 
                     // the key was neither specified nor has it a default value
-                    if(v.empty() && type != Param::STRING) {
+                    if((v.empty() && type != Param::STRING) || v == noneValue) {
                         impl->error = true;
                         impl->error_message = impl->error_message + "Missing parameter: '" + name + "'\n";
                         return;
@@ -133,10 +146,10 @@ void CommandLineParser::getByIndex(int index, bool space_delete, int type, void*
             if (impl->data[i].number == index)
             {
                 String v = impl->data[i].def_value;
-                if (space_delete == true) v = impl->cat_string(v);
+                if (space_delete == true) v = cat_string(v);
 
                 // the key was neither specified nor has it a default value
-                if(v.empty() && type != Param::STRING) {
+                if((v.empty() && type != Param::STRING) || v == noneValue) {
                     impl->error = true;
                     impl->error_message = impl->error_message + format("Missing parameter #%d\n", index);
                     return;
@@ -198,7 +211,7 @@ CommandLineParser::CommandLineParser(int argc, const char* const argv[], const S
         CommandLineParserParams p;
         p.keys = impl->split_string(l[0]);
         p.def_value = l[1];
-        p.help_message = impl->cat_string(l[2]);
+        p.help_message = cat_string(l[2]);
         p.number = -1;
         if (p.keys.size() <= 0)
         {
@@ -317,16 +330,6 @@ void CommandLineParser::Impl::sort_params()
     std::sort (data.begin(), data.end(), cmp_params);
 }
 
-String CommandLineParser::Impl::cat_string(const String& str) const
-{
-    int left = 0, right = (int)str.length();
-    while( left <= right && str[left] == ' ' )
-        left++;
-    while( right > left && str[right-1] == ' ' )
-        right--;
-    return left >= right ? String("") : str.substr(left, right-left);
-}
-
 String CommandLineParser::getPathToApplication() const
 {
     return impl->path_to_app;
@@ -340,7 +343,8 @@ bool CommandLineParser::has(const String& name) const
         {
             if (name == impl->data[i].keys[j])
             {
-                return !impl->cat_string(impl->data[i].def_value).empty();
+                const String v = cat_string(impl->data[i].def_value);
+                return !v.empty() && v != noneValue;
             }
         }
     }
@@ -404,7 +408,7 @@ void CommandLineParser::printMessage() const
                     printf(", ");
                 }
             }
-            String dv = impl->cat_string(impl->data[i].def_value);
+            String dv = cat_string(impl->data[i].def_value);
             if (dv.compare("") != 0)
             {
                 printf(" (value:%s)", dv.c_str());
@@ -424,7 +428,7 @@ void CommandLineParser::printMessage() const
 
             printf("%s", k.c_str());
 
-            String dv = impl->cat_string(impl->data[i].def_value);
+            String dv = cat_string(impl->data[i].def_value);
             if (dv.compare("") != 0)
             {
                 printf(" (value:%s)", dv.c_str());
index f961b93..0f1188d 100644 (file)
@@ -142,9 +142,8 @@ TEST(CommandLineParser, testPositional_noArgs)
     EXPECT_EQ("default1", parser.get<String>("@arg1"));
     EXPECT_EQ("default1", parser.get<String>(0));
 
-    parser.get<String>("@arg2");
-    parser.get<String>(1);
-    EXPECT_TRUE(parser.check());
+    EXPECT_EQ("", parser.get<String>("@arg2"));
+    EXPECT_EQ("", parser.get<String>(1));
 }
 
 TEST(CommandLineParser, testPositional_default)
@@ -186,4 +185,22 @@ TEST(CommandLineParser, testPositional_withFlagsAfter)
     EXPECT_EQ("test2", parser.get<String>(1));
 }
 
+TEST(CommandLineParser, testEmptyStringValue)
+{
+    static const char * const keys3 =
+            "{ @pos0 |        | empty default value }"
+            "{ @pos1 | <none> | forbid empty default value }";
+
+    const char* argv[] = {"<bin>"};
+    const int argc = 1;
+    cv::CommandLineParser parser(argc, argv, keys3);
+    // EXPECT_TRUE(parser.has("@pos0"));
+    EXPECT_EQ("", parser.get<String>("@pos0"));
+    EXPECT_TRUE(parser.check());
+
+    EXPECT_FALSE(parser.has("@pos1"));
+    parser.get<String>(1);
+    EXPECT_FALSE(parser.check());
+}
+
 } // namespace