$include_dir="/home/hyper-archives/boost-commit/include"; include("$include_dir/msg-header.inc") ?>
Subject: [Boost-commit] svn:boost r58263 - in trunk: boost/program_options libs/program_options/src libs/program_options/test
From: s.ochsenknecht_at_[hidden]
Date: 2009-12-10 03:46:45
Author: s_ochsenknecht
Date: 2009-12-10 03:46:44 EST (Thu, 10 Dec 2009)
New Revision: 58263
URL: http://svn.boost.org/trac/boost/changeset/58263
Log:
Enhancement to flag options as required, Fixes #2982
Added:
   trunk/libs/program_options/test/required_test.cfg   (contents, props changed)
   trunk/libs/program_options/test/required_test.cpp   (contents, props changed)
Text files modified: 
   trunk/boost/program_options/errors.hpp            |    17 ++++++++++                              
   trunk/boost/program_options/value_semantic.hpp    |    19 ++++++++++-                             
   trunk/boost/program_options/variables_map.hpp     |     9 +++++                                   
   trunk/libs/program_options/src/value_semantic.cpp |     6 +++                                     
   trunk/libs/program_options/src/variables_map.cpp  |    62 ++++++++++++++++++++++++++++----------- 
   trunk/libs/program_options/test/Jamfile.v2        |     1                                         
   6 files changed, 93 insertions(+), 21 deletions(-)
Modified: trunk/boost/program_options/errors.hpp
==============================================================================
--- trunk/boost/program_options/errors.hpp	(original)
+++ trunk/boost/program_options/errors.hpp	2009-12-10 03:46:44 EST (Thu, 10 Dec 2009)
@@ -213,6 +213,23 @@
          : error(std::string("can not read file ").append(filename))
         {}
     };
+    
+     /** Class thrown when a required/mandatory option is missing */
+     class BOOST_PROGRAM_OPTIONS_DECL required_option : public error {
+     public:
+        required_option(const std::string& name)
+        : error(std::string("missing required option ").append(name))
+        , m_option_name(name)
+        {}
+ 
+        ~required_option() throw() {}
+
+        const std::string& get_option_name() const throw();
+        
+     private:
+        std::string m_option_name; // The name of the option which
+                                   // caused the exception.
+     };
 }}
 
 
Modified: trunk/boost/program_options/value_semantic.hpp
==============================================================================
--- trunk/boost/program_options/value_semantic.hpp	(original)
+++ trunk/boost/program_options/value_semantic.hpp	2009-12-10 03:46:44 EST (Thu, 10 Dec 2009)
@@ -43,6 +43,11 @@
             other sources are discarded.
         */
         virtual bool is_composing() const = 0;
+
+        /** Returns true if value must be given. Non-optional value
+
+        */
+        virtual bool is_required() const = 0;
         
         /** Parses a group of tokens that specify a value of option.
             Stores the result in 'value_store', using whatever representation
@@ -131,6 +136,8 @@
         unsigned max_tokens() const;
 
         bool is_composing() const { return false; }
+
+        bool is_required() const { return false; }
         
         /** If 'value_store' is already initialized, or new_tokens
             has more than one elements, throws. Otherwise, assigns
@@ -177,7 +184,8 @@
             the value when it's known. The parameter can be NULL. */
         typed_value(T* store_to) 
         : m_store_to(store_to), m_composing(false),
-          m_multitoken(false), m_zero_tokens(false)
+          m_multitoken(false), m_zero_tokens(false),
+          m_required(false)
         {} 
 
         /** Specifies default value, which will be used
@@ -266,6 +274,12 @@
             return this;
         }
             
+        /** Specifies that the value must occur. */
+        typed_value* required()
+        {
+            m_required = true;
+            return this;
+        }
 
     public: // value semantic overrides
 
@@ -292,6 +306,7 @@
             }
         }
 
+        bool is_required() const { return m_required; }
 
         /** Creates an instance of the 'validator' class and calls
             its operator() to perform the actual conversion. */
@@ -335,7 +350,7 @@
         std::string m_default_value_as_text;
         boost::any m_implicit_value;
         std::string m_implicit_value_as_text;
-        bool m_composing, m_implicit, m_multitoken, m_zero_tokens;
+        bool m_composing, m_implicit, m_multitoken, m_zero_tokens, m_required;
         boost::function1<void, const T&> m_notifier;
     };
 
Modified: trunk/boost/program_options/variables_map.hpp
==============================================================================
--- trunk/boost/program_options/variables_map.hpp	(original)
+++ trunk/boost/program_options/variables_map.hpp	2009-12-10 03:46:44 EST (Thu, 10 Dec 2009)
@@ -92,7 +92,8 @@
         friend BOOST_PROGRAM_OPTIONS_DECL
         void store(const basic_parsed_options<char>& options, 
               variables_map& m, bool);
-        friend BOOST_PROGRAM_OPTIONS_DECL void notify(variables_map& m);
+
+        friend BOOST_PROGRAM_OPTIONS_DECL class variables_map;
     };
 
     /** Implements string->string mapping with convenient value casting
@@ -147,6 +148,8 @@
         // Resolve conflict between inherited operators.
         const variable_value& operator[](const std::string& name) const
         { return abstract_variables_map::operator[](name); }
+        
+        void notify();
 
     private:
         /** Implementation of abstract_variables_map::get
@@ -161,6 +164,10 @@
         void store(const basic_parsed_options<char>& options, 
                           variables_map& xm,
                           bool utf8);
+        
+        /** Names of required options, filled by parser which has
+            access to options_description. */
+        std::set<std::string> m_required;
     };
 
 
Modified: trunk/libs/program_options/src/value_semantic.cpp
==============================================================================
--- trunk/libs/program_options/src/value_semantic.cpp	(original)
+++ trunk/libs/program_options/src/value_semantic.cpp	2009-12-10 03:46:44 EST (Thu, 10 Dec 2009)
@@ -325,5 +325,11 @@
         }
         return m_message.c_str();
     }
+    
+    const std::string& 
+    required_option::get_option_name() const throw()
+    {
+        return m_option_name;
+    }
 
 }}
Modified: trunk/libs/program_options/src/variables_map.cpp
==============================================================================
--- trunk/libs/program_options/src/variables_map.cpp	(original)
+++ trunk/libs/program_options/src/variables_map.cpp	2009-12-10 03:46:44 EST (Thu, 10 Dec 2009)
@@ -105,7 +105,7 @@
 
         
         
-        // Second, apply default values.
+        // Second, apply default values and store required options.
         const vector<shared_ptr<option_description> >& all = desc.options();
         for(i = 0; i < all.size(); ++i)
         {
@@ -127,7 +127,12 @@
                     m[key] = variable_value(def, true);
                     m[key].m_value_semantic = d.semantic();
                 }
-            }        
+            }  
+
+            // add empty value if this is an required option
+            if (d.semantic()->is_required()) {
+               xm.m_required.insert(key);
+            }
         }
     }
 
@@ -140,22 +145,7 @@
     BOOST_PROGRAM_OPTIONS_DECL 
     void notify(variables_map& vm)
     {        
-        // Lastly, run notify actions.
-        for (map<string, variable_value>::iterator k = vm.begin(); 
-             k != vm.end(); 
-             ++k) 
-        {
-            /* Users might wish to use variables_map to store their own values
-               that are not parsed, and therefore will not have value_semantics
-               defined. Do no crash on such values. In multi-module programs,
-               one module might add custom values, and the 'notify' function
-               will be called after that, so we check that value_sematics is 
-               not NULL. See:
-                   https://svn.boost.org/trac/boost/ticket/2782
-            */
-            if (k->second.m_value_semantic)
-                k->second.m_value_semantic->notify(k->second.value());
-        }               
+        vm.notify();               
     }
 
     abstract_variables_map::abstract_variables_map()
@@ -206,4 +196,40 @@
         else
             return i->second;
     }
+    
+    void
+    variables_map::notify()
+    {
+        // This checks if all required options occur
+        for (set<string>::const_iterator r = m_required.begin();
+             r != m_required.end();
+             ++r)
+        {
+            const string& opt = *r;
+            map<string, variable_value>::const_iterator iter = find(opt);
+            if (iter == end() || iter->second.empty()) 
+            {
+                boost::throw_exception(required_option(opt));
+            
+            }
+        }
+
+        // Lastly, run notify actions.
+        for (map<string, variable_value>::iterator k = begin(); 
+             k != end(); 
+             ++k) 
+        {
+            /* Users might wish to use variables_map to store their own values
+               that are not parsed, and therefore will not have value_semantics
+               defined. Do no crash on such values. In multi-module programs,
+               one module might add custom values, and the 'notify' function
+               will be called after that, so we check that value_sematics is 
+               not NULL. See:
+                   https://svn.boost.org/trac/boost/ticket/2782
+            */
+            if (k->second.m_value_semantic)
+                k->second.m_value_semantic->notify(k->second.value());
+        }               
+    }
+    
 }}
Modified: trunk/libs/program_options/test/Jamfile.v2
==============================================================================
--- trunk/libs/program_options/test/Jamfile.v2	(original)
+++ trunk/libs/program_options/test/Jamfile.v2	2009-12-10 03:46:44 EST (Thu, 10 Dec 2009)
@@ -30,6 +30,7 @@
     [ po-test exception_test.cpp ]
     [ po-test split_test.cpp ]
     [ po-test unrecognized_test.cpp ]
+    [ po-test required_test.cpp ]
     ;
         
 exe test_convert : test_convert.cpp ;   
Added: trunk/libs/program_options/test/required_test.cfg
==============================================================================
--- (empty file)
+++ trunk/libs/program_options/test/required_test.cfg	2009-12-10 03:46:44 EST (Thu, 10 Dec 2009)
@@ -0,0 +1 @@
+cfgfile = file.cfg
Added: trunk/libs/program_options/test/required_test.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/program_options/test/required_test.cpp	2009-12-10 03:46:44 EST (Thu, 10 Dec 2009)
@@ -0,0 +1,97 @@
+// Copyright Sascha Ochsenknecht 2009.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt
+// or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+#include <boost/program_options.hpp>
+using namespace boost::program_options;
+
+#include <string>
+#include <iostream>
+#include <fstream>
+using namespace std;
+
+#include "minitest.hpp"
+
+
+void required_throw_test()
+{
+   options_description		opts;
+   opts.add_options()
+     ("cfgfile,c", value<string>()->required(), "the configfile")
+     ("fritz,f",   value<string>()->required(), "the output file")
+   ;
+
+   variables_map vm;
+   bool throwed = false;
+   {
+      // This test must throw exception
+      string cmdline = "prg -f file.txt";
+      vector< string > tokens =  split_unix(cmdline);
+      throwed = false;
+      try {
+         store(command_line_parser(tokens).options(opts).run(), vm);
+         notify(vm);    
+      } 
+      catch (required_option& e) {
+         throwed = true;
+      }      
+      BOOST_CHECK(throwed);
+   }
+   
+   {
+      // This test mustn't throw exception
+      string cmdline = "prg -c config.txt";
+      vector< string > tokens =  split_unix(cmdline);
+      throwed = false;
+      try {
+         store(command_line_parser(tokens).options(opts).run(), vm);
+         notify(vm);    
+      } 
+      catch (required_option& e) {
+         throwed = true;
+      }
+      BOOST_CHECK(!throwed);
+   }
+}
+
+
+
+void simple_required_test()
+{
+   options_description		opts;
+   opts.add_options()
+     ("cfgfile,c", value<string>()->required(), "the configfile")
+     ("fritz,f",   value<string>()->required(), "the output file")
+   ;
+
+   variables_map vm;
+   bool throwed = false;
+   {
+      // This test must throw exception
+      string cmdline = "prg -f file.txt";
+      vector< string > tokens =  split_unix(cmdline);
+      throwed = false;
+      try {
+         // options coming from different sources
+         store(command_line_parser(tokens).options(opts).run(), vm);
+         store(parse_config_file<char>("required_test.cfg", opts), vm);
+         notify(vm);    
+      } 
+      catch (required_option& e) {
+         throwed = true;
+      }      
+      BOOST_CHECK(!throwed);
+   }
+}
+
+
+
+int main(int /*argc*/, char** /*argv*/)
+{  
+   required_throw_test();
+   simple_required_test();
+   
+   return 0;
+}
+