Boost logo

Boost-Commit :

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;
+}
+


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk