Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r77827 - in trunk: boost/program_options boost/program_options/detail libs/program_options/example libs/program_options/src libs/program_options/test
From: ghost_at_[hidden]
Date: 2012-04-08 04:42:43


Author: vladimir_prus
Date: 2012-04-08 04:42:39 EDT (Sun, 08 Apr 2012)
New Revision: 77827
URL: http://svn.boost.org/trac/boost/changeset/77827

Log:
Improve error reporting.

The name of option is now shown in most cases when it's feasible, and
clarify of the error messages has been improved throughout.

Patch from Leo Goodstadt.

Added:
   trunk/libs/program_options/test/exception_txt_test.cpp (contents, props changed)
Text files modified:
   trunk/boost/program_options/detail/cmdline.hpp | 12 +
   trunk/boost/program_options/detail/parsers.hpp | 6
   trunk/boost/program_options/errors.hpp | 454 +++++++++++++++++++++++++++------------
   trunk/boost/program_options/options_description.hpp | 10
   trunk/boost/program_options/parsers.hpp | 26 ++
   trunk/boost/program_options/variables_map.hpp | 6
   trunk/libs/program_options/example/first.cpp | 4
   trunk/libs/program_options/src/cmdline.cpp | 322 ++++++++++++++++-----------
   trunk/libs/program_options/src/config_file.cpp | 6
   trunk/libs/program_options/src/options_description.cpp | 35 ++
   trunk/libs/program_options/src/parsers.cpp | 5
   trunk/libs/program_options/src/value_semantic.cpp | 285 ++++++++++++++++--------
   trunk/libs/program_options/src/variables_map.cpp | 123 +++++----
   trunk/libs/program_options/test/exception_test.cpp | 12
   trunk/libs/program_options/test/required_test.cpp | 1
   15 files changed, 860 insertions(+), 447 deletions(-)

Modified: trunk/boost/program_options/detail/cmdline.hpp
==============================================================================
--- trunk/boost/program_options/detail/cmdline.hpp (original)
+++ trunk/boost/program_options/detail/cmdline.hpp 2012-04-08 04:42:39 EDT (Sun, 08 Apr 2012)
@@ -81,6 +81,18 @@
         cmdline(int argc, const char*const * argv);
 
         void style(int style);
+
+ /** returns the canonical option prefix associated with the command_line_style
+ * In order of precedence:
+ * allow_long : allow_long
+ * allow_long_disguise : allow_long_disguise
+ * allow_dash_for_short : allow_short | allow_dash_for_short
+ * allow_slash_for_short: allow_short | allow_slash_for_short
+ *
+ * This is mainly used for the diagnostic messages in exceptions
+ */
+ int get_canonical_option_prefix();
+
         void allow_unregistered();
 
         void set_options_description(const options_description& desc);

Modified: trunk/boost/program_options/detail/parsers.hpp
==============================================================================
--- trunk/boost/program_options/detail/parsers.hpp (original)
+++ trunk/boost/program_options/detail/parsers.hpp 2012-04-08 04:42:39 EDT (Sun, 08 Apr 2012)
@@ -100,7 +100,11 @@
     basic_parsed_options<charT>
     basic_command_line_parser<charT>::run()
     {
- parsed_options result(m_desc);
+ // save the canonical prefixes which were used by this cmdline parser
+ // eventually inside the parsed results
+ // This will be handy to format recognisable options
+ // for diagnostic messages if everything blows up much later on
+ parsed_options result(m_desc, detail::cmdline::get_canonical_option_prefix());
         result.options = detail::cmdline::run();
 
         // Presense of parsed_options -> wparsed_options conversion

Modified: trunk/boost/program_options/errors.hpp
==============================================================================
--- trunk/boost/program_options/errors.hpp (original)
+++ trunk/boost/program_options/errors.hpp 2012-04-08 04:42:39 EDT (Sun, 08 Apr 2012)
@@ -12,6 +12,8 @@
 #include <string>
 #include <stdexcept>
 #include <vector>
+#include <map>
+
 
 #if defined(BOOST_MSVC)
 # pragma warning (push)
@@ -21,126 +23,338 @@
 
 namespace boost { namespace program_options {
 
+ inline std::string strip_prefixes(const std::string& text)
+ {
+ return text.substr(text.find_last_of("-/") + 1);
+ }
+
     /** Base class for all errors in the library. */
     class BOOST_PROGRAM_OPTIONS_DECL error : public std::logic_error {
     public:
         error(const std::string& xwhat) : std::logic_error(xwhat) {}
     };
 
- class BOOST_PROGRAM_OPTIONS_DECL invalid_syntax : public error {
+
+ /** Class thrown when there are too many positional options.
+ This is a programming error.
+ */
+ class BOOST_PROGRAM_OPTIONS_DECL too_many_positional_options_error : public error {
     public:
- enum kind_t {
- long_not_allowed = 30,
- long_adjacent_not_allowed,
- short_adjacent_not_allowed,
- empty_adjacent_parameter,
- missing_parameter,
- extra_parameter,
- unrecognized_line
- };
-
- invalid_syntax(const std::string& tokens, kind_t kind);
+ too_many_positional_options_error()
+ : error("too many positional arguments have been specified on the command line")
+ {}
+ };
+
+ /** Class thrown when there are programming error related to style */
+ class BOOST_PROGRAM_OPTIONS_DECL invalid_command_line_style : public error {
+ public:
+ invalid_command_line_style(const std::string& msg)
+ : error(msg)
+ {}
+ };
+
+ /** Class thrown if config file can not be read */
+ class BOOST_PROGRAM_OPTIONS_DECL reading_file : public error {
+ public:
+ reading_file(const char* filename)
+ : error(std::string("can not read options configuration file '").append(filename).append("'"))
+ {}
+ };
+
+
+ /** Base class for most exceptions in the library.
+ *
+ * Substitutes the values for the parameter name
+ * placeholders in the template to create the human
+ * readable error message
+ *
+ * Placeholders are surrounded by % signs: %example%
+ * Poor man's version of boost::format
+ *
+ * If a parameter name is absent, perform default substitutions
+ * instead so ugly placeholders are never left in-place.
+ *
+ * Options are displayed in "canonical" form
+ * This is the most unambiguous form of the
+ * *parsed* option name and would correspond to
+ * option_description::format_name()
+ * i.e. what is shown by print_usage()
+ *
+ * The "canonical" form depends on whether the option is
+ * specified in short or long form, using dashes or slashes
+ * or without a prefix (from a configuration file)
+ *
+ * */
+ class BOOST_PROGRAM_OPTIONS_DECL error_with_option_name : public error {
 
- // gcc says that throw specification on dtor is loosened
- // without this line
- ~invalid_syntax() throw() {}
-
- kind_t kind() const;
-
- const std::string& tokens() const;
-
     protected:
- /** Used to convert kind_t to a related error text */
- static std::string error_message(kind_t kind);
+ /** can be
+ * 0 = no prefix (config file options)
+ * allow_long
+ * allow_dash_for_short
+ * allow_slash_for_short
+ * allow_long_disguise */
+ int m_option_style;
+
+
+ /** substitutions
+ * from placeholders to values */
+ std::map<std::string, std::string> m_substitutions;
+ typedef std::pair<std::string, std::string> string_pair;
+ std::map<std::string, string_pair > m_substitution_defaults;
+
+ public:
+ /** template with placeholders */
+ std::string m_error_template;
+
+ error_with_option_name(const std::string& template_,
+ const std::string& option_name = "",
+ const std::string& original_token = "",
+ int option_style = 0);
+
+ /** gcc says that throw specification on dtor is loosened
+ * without this line
+ * */
+ ~error_with_option_name() throw() {}
+
+
+ //void dump() const
+ //{
+ // std::cerr << "m_substitution_defaults:\n";
+ // for (std::map<std::string, string_pair>::const_iterator iter = m_substitution_defaults.begin();
+ // iter != m_substitution_defaults.end(); ++iter)
+ // std::cerr << "\t" << iter->first << ":" << iter->second.first << "=" << iter->second.second << "\n";
+ // std::cerr << "m_substitutions:\n";
+ // for (std::map<std::string, std::string>::const_iterator iter = m_substitutions.begin();
+ // iter != m_substitutions.end(); ++iter)
+ // std::cerr << "\t" << iter->first << "=" << iter->second << "\n";
+ // std::cerr << "m_error_template:\n";
+ // std::cerr << "\t" << m_error_template << "\n";
+ // std::cerr << "canonical_option_prefix:[" << get_canonical_option_prefix() << "]\n";
+ // std::cerr << "canonical_option_name:[" << get_canonical_option_name() <<"]\n";
+ // std::cerr << "what:[" << what() << "]\n";
+ //}
+
+ /** Substitute
+ * parameter_name->value to create the error message from
+ * the error template */
+ void set_substitute(const std::string& parameter_name, const std::string& value)
+ { m_substitutions[parameter_name] = value; }
+
+ /** If the parameter is missing, then make the
+ * from->to substitution instead */
+ void set_substitute_default(const std::string& parameter_name,
+ const std::string& from,
+ const std::string& to)
+ {
+ m_substitution_defaults[parameter_name] = std::make_pair(from, to);
+ }
+
+
+ /** Add context to an exception */
+ void add_context(const std::string& option_name,
+ const std::string& original_token,
+ int option_style)
+ {
+ set_option_name(option_name);
+ set_original_token(original_token);
+ set_prefix(option_style);
+ }
+
+ void set_prefix(int option_style)
+ { m_option_style = option_style;}
+
+ /** Overridden in error_with_no_option_name */
+ virtual void set_option_name(const std::string& option_name)
+ { set_substitute("option", option_name);}
+
+ std::string get_option_name() const throw()
+ { return get_canonical_option_name(); }
+
+ void set_original_token(const std::string& original_token)
+ { set_substitute("original_token", original_token);}
+
+
+ /** Creates the error_message on the fly
+ * Currently a thin wrapper for substitute_placeholders() */
+ virtual const char* what() const throw();
 
- private:
- // TODO: copy ctor might throw
- std::string m_tokens;
+ protected:
+ /** Used to hold the error text returned by what() */
+ mutable std::string m_message; // For on-demand formatting in 'what'
 
- kind_t m_kind;
+ /** Makes all substitutions using the template */
+ virtual void substitute_placeholders(const std::string& error_template) const;
+
+ // helper function for substitute_placeholders
+ void replace_token(const std::string& from, const std::string& to) const;
+
+ /** Construct option name in accordance with the appropriate
+ * prefix style: i.e. long dash or short slash etc */
+ std::string get_canonical_option_name() const;
+ std::string get_canonical_option_prefix() const;
+ };
+
+
+ /** Class thrown when there are several option values, but
+ user called a method which cannot return them all. */
+ class BOOST_PROGRAM_OPTIONS_DECL multiple_values : public error_with_option_name {
+ public:
+ multiple_values()
+ : error_with_option_name("option '%canonical_option%' only takes a single argument"){}
+
+ ~multiple_values() throw() {}
+ };
+
+ /** Class thrown when there are several occurrences of an
+ option, but user called a method which cannot return
+ them all. */
+ class BOOST_PROGRAM_OPTIONS_DECL multiple_occurrences : public error_with_option_name {
+ public:
+ multiple_occurrences()
+ : error_with_option_name("option '%canonical_option%' cannot be specified more than once"){}
+
+ ~multiple_occurrences() throw() {}
+
+ };
+
+ /** Class thrown when a required/mandatory option is missing */
+ class BOOST_PROGRAM_OPTIONS_DECL required_option : public error_with_option_name {
+ public:
+ // option name is constructed by the option_descriptor and never on the fly
+ required_option(const std::string& option_name)
+ : error_with_option_name("the option '%canonical_option%' is required but missing", "", option_name)
+ {
+ }
+
+ ~required_option() throw() {}
+ };
+
+ /** Base class of unparsable options,
+ * when the desired option cannot be identified.
+ *
+ *
+ * It makes no sense to have an option name, when we can't match an option to the
+ * parameter
+ *
+ * Having this a part of the error_with_option_name hierachy makes error handling
+ * a lot easier, even if the name indicates some sort of conceptual dissonance!
+ *
+ * */
+ class BOOST_PROGRAM_OPTIONS_DECL error_with_no_option_name : public error_with_option_name {
+ public:
+ error_with_no_option_name(const std::string& template_,
+ const std::string& original_token = "")
+ : error_with_option_name(template_, "", original_token)
+ {
+ }
+
+ /** Does NOT set option name, because no option name makes sense */
+ virtual void set_option_name(const std::string& option_name){}
+
+ ~error_with_no_option_name() throw() {}
     };
 
+
     /** Class thrown when option name is not recognized. */
- class BOOST_PROGRAM_OPTIONS_DECL unknown_option : public error {
+ class BOOST_PROGRAM_OPTIONS_DECL unknown_option : public error_with_no_option_name {
     public:
- unknown_option(const std::string& name)
- : error(std::string("unknown option ").append(name)),
- m_option_name(name)
- {}
+ unknown_option(const std::string& original_token = "")
+ : error_with_no_option_name("unrecognised option '%canonical_option%'", original_token)
+ {
+ }
 
- // gcc says that throw specification on dtor is loosened
- // without this line
         ~unknown_option() throw() {}
-
- const std::string& get_option_name() const throw();
-
- private:
- std::string m_option_name;
     };
 
+
+
     /** Class thrown when there's ambiguity amoung several possible options. */
- class BOOST_PROGRAM_OPTIONS_DECL ambiguous_option : public error {
+ class BOOST_PROGRAM_OPTIONS_DECL ambiguous_option : public error_with_no_option_name {
     public:
- ambiguous_option(const std::string& name,
- const std::vector<std::string>& xalternatives)
- : error(std::string("ambiguous option ").append(name))
- , m_alternatives(xalternatives)
- , m_option_name(name)
+ ambiguous_option(const std::vector<std::string>& xalternatives)
+ : error_with_no_option_name("option '%canonical_option%' is ambiguous"),
+ m_alternatives(xalternatives)
         {}
 
         ~ambiguous_option() throw() {}
-
- const std::string& get_option_name() const throw();
-
- const std::vector<std::string>& alternatives() const throw();
 
+ const std::vector<std::string>& alternatives() const throw() {return m_alternatives;}
+
+ protected:
+ /** Makes all substitutions using the template */
+ virtual void substitute_placeholders(const std::string& error_template) const;
     private:
         // TODO: copy ctor might throw
         std::vector<std::string> m_alternatives;
- std::string m_option_name;
     };
 
- /** Class thrown when there are several option values, but
- user called a method which cannot return them all. */
- class BOOST_PROGRAM_OPTIONS_DECL multiple_values : public error {
+
+ /** Class thrown when there's syntax error either for command
+ * line or config file options. See derived children for
+ * concrete classes. */
+ class BOOST_PROGRAM_OPTIONS_DECL invalid_syntax : public error_with_option_name {
     public:
- multiple_values()
- : error("multiple values")
- , m_option_name() {}
-
- ~multiple_values() throw() {}
-
- void set_option_name(const std::string& option);
-
- const std::string& get_option_name() const throw();
-
- private:
- std::string m_option_name; // The name of the option which
- // caused the exception.
+ enum kind_t {
+ long_not_allowed = 30,
+ long_adjacent_not_allowed,
+ short_adjacent_not_allowed,
+ empty_adjacent_parameter,
+ missing_parameter,
+ extra_parameter,
+ unrecognized_line
+ };
+
+ invalid_syntax(kind_t kind,
+ const std::string& option_name = "",
+ const std::string& original_token = "",
+ int option_style = 0):
+ error_with_option_name(get_template(kind), option_name, original_token, option_style),
+ m_kind(kind)
+ {
+ }
+
+ ~invalid_syntax() throw() {}
+
+ kind_t kind() const {return m_kind;}
+
+ /** Convenience functions for backwards compatibility */
+ virtual std::string tokens() const {return get_option_name(); }
+ protected:
+ /** Used to convert kind_t to a related error text */
+ std::string get_template(kind_t kind);
+ kind_t m_kind;
     };
 
- /** Class thrown when there are several occurrences of an
- option, but user called a method which cannot return
- them all. */
- class BOOST_PROGRAM_OPTIONS_DECL multiple_occurrences : public error {
+ class BOOST_PROGRAM_OPTIONS_DECL invalid_config_file_syntax : public invalid_syntax {
     public:
- multiple_occurrences()
- : error("multiple occurrences")
- , m_option_name() {}
-
- ~multiple_occurrences() throw() {}
-
- void set_option_name(const std::string& option);
-
- const std::string& get_option_name() const throw();
+ invalid_config_file_syntax(const std::string& invalid_line, kind_t kind):
+ invalid_syntax(kind)
+ {
+ m_substitutions["invalid_line"] = invalid_line;
+ }
 
- private:
- std::string m_option_name; // The name of the option which
- // caused the exception.
+ ~invalid_config_file_syntax() throw() {}
+
+ /** Convenience functions for backwards compatibility */
+ virtual std::string tokens() const {return m_substitutions.find("invalid_line")->second; }
     };
 
+
+ /** Class thrown when there are syntax errors in given command line */
+ class BOOST_PROGRAM_OPTIONS_DECL invalid_command_line_syntax : public invalid_syntax {
+ public:
+ invalid_command_line_syntax(kind_t kind,
+ const std::string& option_name = "",
+ const std::string& original_token = "",
+ int option_style = 0):
+ invalid_syntax(kind, option_name, original_token, option_style) {}
+ ~invalid_command_line_syntax() throw() {}
+ };
+
+
     /** Class thrown when value of option is incorrect. */
- class BOOST_PROGRAM_OPTIONS_DECL validation_error : public error {
+ class BOOST_PROGRAM_OPTIONS_DECL validation_error : public error_with_option_name {
     public:
         enum kind_t {
             multiple_values_not_allowed = 30,
@@ -150,32 +364,24 @@
             invalid_option
         };
         
+ public:
         validation_error(kind_t kind,
- const std::string& option_value = "",
- const std::string& option_name = "");
-
+ const std::string& option_name = "",
+ const std::string& original_token = "",
+ int option_style = 0):
+ error_with_option_name(get_template(kind), option_name, original_token, option_style)
+ {
+ }
+
         ~validation_error() throw() {}
 
- void set_option_name(const std::string& option);
-
- const std::string& get_option_name() const throw();
-
- const char* what() const throw();
-
     protected:
         /** Used to convert kind_t to a related error text */
- static std::string error_message(kind_t kind);
-
- private:
+ std::string get_template(kind_t kind);
         kind_t m_kind;
- std::string m_option_name; // The name of the option which
- // caused the exception.
- std::string m_option_value; // Optional: value of the option m_options_name
- mutable std::string m_message; // For on-demand formatting in 'what'
-
     };
 
- /** Class thrown if there is an invalid option value givenn */
+ /** Class thrown if there is an invalid option value given */
     class BOOST_PROGRAM_OPTIONS_DECL invalid_option_value
         : public validation_error
     {
@@ -186,54 +392,20 @@
 #endif
     };
 
- /** Class thrown when there are too many positional options.
- This is a programming error.
- */
- class BOOST_PROGRAM_OPTIONS_DECL too_many_positional_options_error : public error {
+ /** Class thrown if there is an invalid bool value given */
+ class BOOST_PROGRAM_OPTIONS_DECL invalid_bool_value
+ : public validation_error
+ {
     public:
- too_many_positional_options_error()
- : error("too many positional options")
- {}
+ invalid_bool_value(const std::string& value);
     };
 
- /** Class thrown when there are syntax errors in given command line */
- class BOOST_PROGRAM_OPTIONS_DECL invalid_command_line_syntax : public invalid_syntax {
- public:
- invalid_command_line_syntax(const std::string& tokens, kind_t kind);
- };
 
- /** Class thrown when there are programming error related to style */
- class BOOST_PROGRAM_OPTIONS_DECL invalid_command_line_style : public error {
- public:
- invalid_command_line_style(const std::string& msg)
- : error(msg)
- {}
- };
 
- /** Class thrown if config file can not be read */
- class BOOST_PROGRAM_OPTIONS_DECL reading_file : public error {
- public:
- reading_file(const char* filename)
- : 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.
- };
 }}
 
 #if defined(BOOST_MSVC)

Modified: trunk/boost/program_options/options_description.hpp
==============================================================================
--- trunk/boost/program_options/options_description.hpp (original)
+++ trunk/boost/program_options/options_description.hpp 2012-04-08 04:42:39 EDT (Sun, 08 Apr 2012)
@@ -102,6 +102,16 @@
         */
         const std::string& key(const std::string& option) const;
 
+
+ /** Returns the canonical name for the option description to enable the user to
+ recognised a matching option.
+ 1) For short options ('-', '/'), returns the short name prefixed.
+ 2) For long options ('--' / '-') returns the long name prefixed
+ 3) All other cases, returns the long name (if present) or the short name,
+ unprefixed.
+ */
+ std::string canonical_display_name(int canonical_option_style = 0) const;
+
         const std::string& long_name() const;
 
         /// Explanation of this option

Modified: trunk/boost/program_options/parsers.hpp
==============================================================================
--- trunk/boost/program_options/parsers.hpp (original)
+++ trunk/boost/program_options/parsers.hpp 2012-04-08 04:42:39 EDT (Sun, 08 Apr 2012)
@@ -36,8 +36,8 @@
     template<class charT>
     class basic_parsed_options {
     public:
- explicit basic_parsed_options(const options_description* xdescription)
- : description(xdescription) {}
+ explicit basic_parsed_options(const options_description* xdescription, int options_prefix = 0)
+ : description(xdescription), m_options_prefix(options_prefix) {}
         /** Options found in the source. */
         std::vector< basic_option<charT> > options;
         /** Options description that was used for parsing.
@@ -46,6 +46,17 @@
             up to the caller. Can be NULL.
          */
         const options_description* description;
+
+ /** Mainly used for the diagnostic messages in exceptions.
+ * The canonical option prefix for the parser which generated these results,
+ * depending on the settings for basic_command_line_parser::style() or
+ * cmdline::style(). In order of precedence of command_line_style enums:
+ * allow_long
+ * allow_long_disguise
+ * allow_dash_for_short
+ * allow_slash_for_short
+ */
+ int m_options_prefix;
     };
 
     /** Specialization of basic_parsed_options which:
@@ -64,6 +75,17 @@
         /** Stores UTF8 encoded options that were passed to constructor,
             to avoid reverse conversion in some cases. */
         basic_parsed_options<char> utf8_encoded_options;
+
+ /** Mainly used for the diagnostic messages in exceptions.
+ * The canonical option prefix for the parser which generated these results,
+ * depending on the settings for basic_command_line_parser::style() or
+ * cmdline::style(). In order of precedence of command_line_style enums:
+ * allow_long
+ * allow_long_disguise
+ * allow_dash_for_short
+ * allow_slash_for_short
+ */
+ int m_options_prefix;
     };
 
     typedef basic_parsed_options<char> parsed_options;

Modified: trunk/boost/program_options/variables_map.hpp
==============================================================================
--- trunk/boost/program_options/variables_map.hpp (original)
+++ trunk/boost/program_options/variables_map.hpp 2012-04-08 04:42:39 EDT (Sun, 08 Apr 2012)
@@ -174,8 +174,10 @@
                           bool utf8);
         
         /** Names of required options, filled by parser which has
- access to options_description. */
- std::set<std::string> m_required;
+ access to options_description.
+ The map values are the "canonical" names for each corresponding option.
+ This is useful in creating diagnostic messages when the option is absent. */
+ std::map<std::string, std::string> m_required;
     };
 
 

Modified: trunk/libs/program_options/example/first.cpp
==============================================================================
--- trunk/libs/program_options/example/first.cpp (original)
+++ trunk/libs/program_options/example/first.cpp 2012-04-08 04:42:39 EDT (Sun, 08 Apr 2012)
@@ -20,7 +20,7 @@
         po::options_description desc("Allowed options");
         desc.add_options()
             ("help", "produce help message")
- ("compression", po::value<int>(), "set compression level")
+ ("compression", po::value<double>(), "set compression level")
         ;
 
         po::variables_map vm;
@@ -34,7 +34,7 @@
 
         if (vm.count("compression")) {
             cout << "Compression level was set to "
- << vm["compression"].as<int>() << ".\n";
+ << vm["compression"].as<double>() << ".\n";
         } else {
             cout << "Compression level was not set.\n";
         }

Modified: trunk/libs/program_options/src/cmdline.cpp
==============================================================================
--- trunk/libs/program_options/src/cmdline.cpp (original)
+++ trunk/libs/program_options/src/cmdline.cpp 2012-04-08 04:42:39 EDT (Sun, 08 Apr 2012)
@@ -34,64 +34,43 @@
     using namespace std;
     using namespace boost::program_options::command_line_style;
     
- invalid_syntax::
- invalid_syntax(const string& tokens, kind_t kind)
- : error(error_message(kind).append(" in '").append(tokens).append("'"))
- , m_tokens(tokens)
- , m_kind(kind)
- {}
     
     string
- invalid_syntax::error_message(kind_t kind)
+ invalid_syntax::get_template(kind_t kind)
     {
         // Initially, store the message in 'const char*' variable,
         // to avoid conversion to string in all cases.
         const char* msg;
         switch(kind)
         {
+ case empty_adjacent_parameter:
+ msg = "the argument for option '%canonical_option%' should follow immediately after the equal sign";
+ break;
+ case missing_parameter:
+ msg = "the required argument for option '%canonical_option%' is missing";
+ break;
+ case unrecognized_line:
+ msg = "the options configuration file contains an invalid line '%invalid_line%'";
+ break;
+ // none of the following are currently used:
         case long_not_allowed:
- msg = "long options are not allowed";
+ msg = "the unabbreviated option '%canonical_option%' is not valid";
             break;
         case long_adjacent_not_allowed:
- msg = "parameters adjacent to long options not allowed";
+ msg = "the unabbreviated option '%canonical_option%' does not take any arguments";
             break;
         case short_adjacent_not_allowed:
- msg = "parameters adjust to short options are not allowed";
- break;
- case empty_adjacent_parameter:
- msg = "adjacent parameter is empty";
- break;
- case missing_parameter:
- msg = "required parameter is missing";
+ msg = "the abbreviated option '%canonical_option%' does not take any arguments";
             break;
         case extra_parameter:
- msg = "extra parameter";
- break;
- case unrecognized_line:
- msg = "unrecognized line";
+ msg = "option '%canonical_option%' does not take any arguments";
             break;
         default:
- msg = "unknown error";
+ msg = "unknown command line syntax error for '%s'";
         }
         return msg;
     }
 
- invalid_syntax::kind_t
- invalid_syntax::kind() const
- {
- return m_kind;
- }
-
- const string&
- invalid_syntax::tokens() const
- {
- return m_tokens;
- }
-
- invalid_command_line_syntax::
- invalid_command_line_syntax(const string& tokens, kind_t kind)
- : invalid_syntax(tokens, kind)
- {}
 
 }}
 
@@ -156,15 +135,26 @@
         const char* error = 0;
         if (allow_some_long &&
             !(style & long_allow_adjacent) && !(style & long_allow_next))
- error = "style disallows parameters for long options";
+ error = "boost::program_options misconfiguration: "
+ "choose one or other of 'command_line_style::long_allow_next' "
+ "(whitespace separated arguments) or "
+ "'command_line_style::long_allow_adjacent' ('=' separated arguments) for "
+ "long options.";
 
         if (!error && (style & allow_short) &&
             !(style & short_allow_adjacent) && !(style & short_allow_next))
- error = "style disallows parameters for short options";
+ error = "boost::program_options misconfiguration: "
+ "choose one or other of 'command_line_style::short_allow_next' "
+ "(whitespace separated arguments) or "
+ "'command_line_style::short_allow_adjacent' ('=' separated arguments) for "
+ "short options.";
 
         if (!error && (style & allow_short) &&
             !(style & allow_dash_for_short) && !(style & allow_slash_for_short))
- error = "style disallows all characters for short options";
+ error = "boost::program_options misconfiguration: "
+ "choose one or other of 'command_line_style::allow_slash_for_short' "
+ "(slashes) or 'command_line_style::allow_dash_for_short' (dashes) for "
+ "short options.";
 
         if (error)
             boost::throw_exception(invalid_command_line_style(error));
@@ -192,6 +182,23 @@
         m_positional = &positional;
     }
 
+ int
+ cmdline::get_canonical_option_prefix()
+ {
+ if (m_style & allow_long)
+ return allow_long;
+
+ if (m_style & allow_long_disguise)
+ return allow_long_disguise;
+
+ if ((m_style & allow_short) && (m_style & allow_dash_for_short))
+ return allow_dash_for_short;
+
+ if ((m_style & allow_short) && (m_style & allow_slash_for_short))
+ return allow_slash_for_short;
+
+ return 0;
+ }
 
     vector<option>
     cmdline::run()
@@ -288,11 +295,21 @@
             if (opt.string_key.empty())
                 continue;
 
- const option_description* xd =
- m_desc->find_nothrow(opt.string_key,
- is_style_active(allow_guessing),
- is_style_active(long_case_insensitive),
- is_style_active(short_case_insensitive));
+ const option_description* xd;
+ try
+ {
+ xd = m_desc->find_nothrow(opt.string_key,
+ is_style_active(allow_guessing),
+ is_style_active(long_case_insensitive),
+ is_style_active(short_case_insensitive));
+ }
+ catch(error_with_option_name& e)
+ {
+ // add context and rethrow
+ e.add_context(opt.string_key, opt.original_tokens[0], get_canonical_option_prefix());
+ throw;
+ }
+
             if (!xd)
                 continue;
 
@@ -383,92 +400,112 @@
         if (opt.string_key.empty())
             return;
 
- // First check that the option is valid, and get its description.
- const option_description* xd = m_desc->find_nothrow(opt.string_key,
- is_style_active(allow_guessing),
- is_style_active(long_case_insensitive),
- is_style_active(short_case_insensitive));
+ //
+ // Be defensive:
+ // will have no original token if option created by handle_additional_parser()
+ std::string original_token_for_exceptions = opt.string_key;
+ if (opt.original_tokens.size())
+ original_token_for_exceptions = opt.original_tokens[0];
 
- if (!xd)
+ try
         {
- if (m_allow_unregistered) {
- opt.unregistered = true;
- return;
- } else {
- boost::throw_exception(unknown_option(opt.string_key));
- }
- }
- const option_description& d = *xd;
-
- // Canonize the name
- opt.string_key = d.key(opt.string_key);
-
- // We check that the min/max number of tokens for the option
- // agrees with the number of tokens we have. The 'adjacent_value'
- // (the value in --foo=1) counts as a separate token, and if present
- // must be consumed. The following tokens on the command line may be
- // left unconsumed.
+ // First check that the option is valid, and get its description.
+ const option_description* xd = m_desc->find_nothrow(opt.string_key,
+ is_style_active(allow_guessing),
+ is_style_active(long_case_insensitive),
+ is_style_active(short_case_insensitive));
 
- // We don't check if those tokens look like option, or not!
-
- unsigned min_tokens = d.semantic()->min_tokens();
- unsigned max_tokens = d.semantic()->max_tokens();
-
- unsigned present_tokens = opt.value.size() + other_tokens.size();
-
- if (present_tokens >= min_tokens)
- {
- if (!opt.value.empty() && max_tokens == 0)
- {
- boost::throw_exception(invalid_command_line_syntax(opt.string_key,
- invalid_command_line_syntax::extra_parameter));
- }
-
- // If an option wants, at minimum, N tokens, we grab them there,
- // when adding these tokens as values to current option we check
- // if they look like options
- if (opt.value.size() <= min_tokens)
- {
- min_tokens -= opt.value.size();
- }
- else
+ if (!xd)
             {
- min_tokens = 0;
+ if (m_allow_unregistered) {
+ opt.unregistered = true;
+ return;
+ } else {
+ boost::throw_exception(unknown_option());
+ }
             }
+ const option_description& d = *xd;
+
+ // Canonize the name
+ opt.string_key = d.key(opt.string_key);
+
+ // We check that the min/max number of tokens for the option
+ // agrees with the number of tokens we have. The 'adjacent_value'
+ // (the value in --foo=1) counts as a separate token, and if present
+ // must be consumed. The following tokens on the command line may be
+ // left unconsumed.
+
+ // We don't check if those tokens look like option, or not!
 
- // Everything's OK, move the values to the result.
- for(;!other_tokens.empty() && min_tokens--; )
+ unsigned min_tokens = d.semantic()->min_tokens();
+ unsigned max_tokens = d.semantic()->max_tokens();
+
+ unsigned present_tokens = opt.value.size() + other_tokens.size();
+
+ if (present_tokens >= min_tokens)
             {
- // check if extra parameter looks like a known option
- // we use style parsers to check if it is syntactically an option,
- // additionally we check if an option_description exists
- vector<option> followed_option;
- vector<string> next_token(1, other_tokens[0]);
- for (unsigned i = 0; followed_option.empty() && i < style_parsers.size(); ++i)
+ if (!opt.value.empty() && max_tokens == 0)
                 {
- followed_option = style_parsers[i](next_token);
+ boost::throw_exception(
+ invalid_command_line_syntax(invalid_command_line_syntax::extra_parameter));
                 }
- if (!followed_option.empty())
+
+ // If an option wants, at minimum, N tokens, we grab them there,
+ // when adding these tokens as values to current option we check
+ // if they look like options
+ if (opt.value.size() <= min_tokens)
                 {
- const option_description* od = m_desc->find_nothrow(other_tokens[0],
- is_style_active(allow_guessing),
- is_style_active(long_case_insensitive),
- is_style_active(short_case_insensitive));
- if (od)
- boost::throw_exception(invalid_command_line_syntax(opt.string_key,
- invalid_command_line_syntax::missing_parameter));
+ min_tokens -= opt.value.size();
+ }
+ else
+ {
+ min_tokens = 0;
+ }
+
+ // Everything's OK, move the values to the result.
+ for(;!other_tokens.empty() && min_tokens--; )
+ {
+ // check if extra parameter looks like a known option
+ // we use style parsers to check if it is syntactically an option,
+ // additionally we check if an option_description exists
+ vector<option> followed_option;
+ vector<string> next_token(1, other_tokens[0]);
+ for (unsigned i = 0; followed_option.empty() && i < style_parsers.size(); ++i)
+ {
+ followed_option = style_parsers[i](next_token);
+ }
+ if (!followed_option.empty())
+ {
+ original_token_for_exceptions = other_tokens[0];
+ const option_description* od = m_desc->find_nothrow(other_tokens[0],
+ is_style_active(allow_guessing),
+ is_style_active(long_case_insensitive),
+ is_style_active(short_case_insensitive));
+ if (od)
+ boost::throw_exception(
+ invalid_command_line_syntax(invalid_command_line_syntax::missing_parameter));
+ }
+ opt.value.push_back(other_tokens[0]);
+ opt.original_tokens.push_back(other_tokens[0]);
+ other_tokens.erase(other_tokens.begin());
                 }
- opt.value.push_back(other_tokens[0]);
- opt.original_tokens.push_back(other_tokens[0]);
- other_tokens.erase(other_tokens.begin());
             }
- }
- else
- {
- boost::throw_exception(invalid_command_line_syntax(opt.string_key,
- invalid_command_line_syntax::missing_parameter));
+ else
+ {
+ boost::throw_exception(
+ invalid_command_line_syntax(invalid_command_line_syntax::missing_parameter));
 
+ }
+ }
+ // use only original token for unknown_option / ambiguous_option since by definition
+ // they are unrecognised / unparsable
+ catch(error_with_option_name& e)
+ {
+ // add context and rethrow
+ e.add_context(opt.string_key, original_token_for_exceptions, get_canonical_option_prefix());
+ throw;
         }
+
     }
 
     vector<option>
@@ -486,8 +523,11 @@
                 name = tok.substr(2, p-2);
                 adjacent = tok.substr(p+1);
                 if (adjacent.empty())
- boost::throw_exception( invalid_command_line_syntax(name,
- invalid_command_line_syntax::empty_adjacent_parameter) );
+ boost::throw_exception( invalid_command_line_syntax(
+ invalid_command_line_syntax::empty_adjacent_parameter,
+ name,
+ name,
+ get_canonical_option_prefix()) );
             }
             else
             {
@@ -523,9 +563,20 @@
             // of token is considered to be value, not further grouped
             // option.
             for(;;) {
- const option_description* d
- = m_desc->find_nothrow(name, false, false,
- is_style_active(short_case_insensitive));
+ const option_description* d;
+ try
+ {
+
+ d = m_desc->find_nothrow(name, false, false,
+ is_style_active(short_case_insensitive));
+ }
+ catch(error_with_option_name& e)
+ {
+ // add context and rethrow
+ e.add_context(name, name, get_canonical_option_prefix());
+ throw;
+ }
+
 
                 // FIXME: check for 'allow_sticky'.
                 if (d && (m_style & allow_sticky) &&
@@ -589,15 +640,24 @@
             ((tok[0] == '-' && tok[1] != '-') ||
              ((m_style & allow_slash_for_short) && tok[0] == '/')))
         {
- if (m_desc->find_nothrow(tok.substr(1, tok.find('=')-1),
- is_style_active(allow_guessing),
- is_style_active(long_case_insensitive),
- is_style_active(short_case_insensitive)))
- {
- args[0].insert(0, "-");
- if (args[0][1] == '/')
- args[0][1] = '-';
- return parse_long_option(args);
+ try
+ {
+ if (m_desc->find_nothrow(tok.substr(1, tok.find('=')-1),
+ is_style_active(allow_guessing),
+ is_style_active(long_case_insensitive),
+ is_style_active(short_case_insensitive)))
+ {
+ args[0].insert(0, "-");
+ if (args[0][1] == '/')
+ args[0][1] = '-';
+ return parse_long_option(args);
+ }
+ }
+ catch(error_with_option_name& e)
+ {
+ // add context and rethrow
+ e.add_context(tok, tok, get_canonical_option_prefix());
+ throw;
             }
         }
         return vector<option>();

Modified: trunk/libs/program_options/src/config_file.cpp
==============================================================================
--- trunk/libs/program_options/src/config_file.cpp (original)
+++ trunk/libs/program_options/src/config_file.cpp 2012-04-08 04:42:39 EDT (Sun, 08 Apr 2012)
@@ -57,7 +57,9 @@
                     bad_prefixes = true;
             }
             if (bad_prefixes)
- boost::throw_exception(error("bad prefixes"));
+ boost::throw_exception(error("options '" + string(name) + "' and '" +
+ *i + "*' will both match the same "
+ "arguments from the configuration file"));
             allowed_prefixes.insert(s);
         }
     }
@@ -117,7 +119,7 @@
                     break;
 
                 } else {
- boost::throw_exception(invalid_syntax(s, invalid_syntax::unrecognized_line));
+ boost::throw_exception(invalid_config_file_syntax(s, invalid_syntax::unrecognized_line));
                 }
             }
         }

Modified: trunk/libs/program_options/src/options_description.cpp
==============================================================================
--- trunk/libs/program_options/src/options_description.cpp (original)
+++ trunk/libs/program_options/src/options_description.cpp 2012-04-08 04:42:39 EDT (Sun, 08 Apr 2012)
@@ -137,6 +137,31 @@
             return m_short_name;
     }
 
+ std::string
+ option_description::canonical_display_name(int prefix_style) const
+ {
+ if (!m_long_name.empty())
+ {
+ if (prefix_style == command_line_style::allow_long)
+ return "--" + m_long_name;
+ if (prefix_style == command_line_style::allow_long_disguise)
+ return "-" + m_long_name;
+ }
+ // sanity check: m_short_name[0] should be '-' or '/'
+ if (m_short_name.length() == 2)
+ {
+ if (prefix_style == command_line_style::allow_slash_for_short)
+ return string("/") + m_short_name[1];
+ if (prefix_style == command_line_style::allow_dash_for_short)
+ return string("-") + m_short_name[1];
+ }
+ if (!m_long_name.empty())
+ return m_long_name;
+ else
+ return m_short_name;
+ }
+
+
     const std::string&
     option_description::long_name() const
     {
@@ -292,7 +317,7 @@
         const option_description* d = find_nothrow(name, approx,
                                        long_ignore_case, short_ignore_case);
         if (!d)
- boost::throw_exception(unknown_option(name));
+ boost::throw_exception(unknown_option());
         return *d;
     }
 
@@ -340,8 +365,7 @@
             }
         }
         if (full_matches.size() > 1)
- boost::throw_exception(
- ambiguous_option(name, full_matches));
+ boost::throw_exception(ambiguous_option(full_matches));
         
         // If we have a full match, and an approximate match,
         // ignore approximate match instead of reporting error.
@@ -349,8 +373,7 @@
         // "--all" on the command line should select the first one,
         // without ambiguity.
         if (full_matches.empty() && approximate_matches.size() > 1)
- boost::throw_exception(
- ambiguous_option(name, approximate_matches));
+ boost::throw_exception(ambiguous_option(approximate_matches));
 
         return found.get();
     }
@@ -399,7 +422,7 @@
                 if (count(par.begin(), par.end(), '\t') > 1)
                 {
                     boost::throw_exception(program_options::error(
- "Only one tab per paragraph is allowed"));
+ "Only one tab per paragraph is allowed in the options description"));
                 }
           
                 // erase tab from string

Modified: trunk/libs/program_options/src/parsers.cpp
==============================================================================
--- trunk/libs/program_options/src/parsers.cpp (original)
+++ trunk/libs/program_options/src/parsers.cpp 2012-04-08 04:42:39 EDT (Sun, 08 Apr 2012)
@@ -88,7 +88,8 @@
     basic_parsed_options<wchar_t>
     ::basic_parsed_options(const parsed_options& po)
     : description(po.description),
- utf8_encoded_options(po)
+ utf8_encoded_options(po),
+ m_options_prefix(po.m_options_prefix)
     {
         for (unsigned i = 0; i < po.options.size(); ++i)
             options.push_back(woption_from_option(po.options[i]));
@@ -110,7 +111,7 @@
 
             if (d.long_name().empty())
                 boost::throw_exception(
- error("long name required for config file"));
+ error("abbreviated option names are not permitted in options configuration files"));
 
             allowed_options.insert(d.long_name());
         }

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 2012-04-08 04:42:39 EDT (Sun, 08 Apr 2012)
@@ -7,6 +7,8 @@
 #include <boost/program_options/config.hpp>
 #include <boost/program_options/value_semantic.hpp>
 #include <boost/program_options/detail/convert.hpp>
+#include <boost/program_options/detail/cmdline.hpp>
+#include <set>
 
 #include <cctype>
 
@@ -14,6 +16,22 @@
 
     using namespace std;
 
+
+#ifndef BOOST_NO_STD_WSTRING
+ namespace
+ {
+ std::string convert_value(const std::wstring& s)
+ {
+ try {
+ return to_local_8_bit(s);
+ }
+ catch(const std::exception&) {
+ return "<unrepresentable unicode string>";
+ }
+ }
+ }
+#endif
+
     void
     value_semantic_codecvt_helper<char>::
     parse(boost::any& value_store,
@@ -139,7 +157,7 @@
         else if (s == "off" || s == "no" || s == "0" || s == "false")
             v = any(false);
         else
- boost::throw_exception(validation_error(validation_error::invalid_bool_value, s));
+ boost::throw_exception(invalid_bool_value(s));
     }
 
     // This is blatant copy-paste. However, templating this will cause a problem,
@@ -161,7 +179,7 @@
         else if (s == L"off" || s == L"no" || s == L"0" || s == L"false")
             v = any(false);
         else
- boost::throw_exception(validation_error(validation_error::invalid_bool_value));
+ boost::throw_exception(invalid_bool_value(convert_value(s)));
     }
 #endif
     BOOST_PROGRAM_OPTIONS_DECL
@@ -194,120 +212,212 @@
 
     invalid_option_value::
     invalid_option_value(const std::string& bad_value)
- : validation_error(validation_error::invalid_option_value, bad_value)
- {}
-
-#ifndef BOOST_NO_STD_WSTRING
- namespace
+ : validation_error(validation_error::invalid_option_value)
     {
- std::string convert_value(const std::wstring& s)
- {
- try {
- return to_local_8_bit(s);
- }
- catch(const std::exception&) {
- return "<unrepresentable unicode string>";
- }
- }
+ set_substitute("value", bad_value);
     }
 
+#ifndef BOOST_NO_STD_WSTRING
     invalid_option_value::
     invalid_option_value(const std::wstring& bad_value)
- : validation_error(validation_error::invalid_option_value, convert_value(bad_value))
- {}
+ : validation_error(validation_error::invalid_option_value)
+ {
+ set_substitute("value", convert_value(bad_value));
+ }
 #endif
- const std::string&
- unknown_option::get_option_name() const throw()
- {
- return m_option_name;
- }
-
- const std::string&
- ambiguous_option::get_option_name() const throw()
- {
- return m_option_name;
- }
-
- const std::vector<std::string>&
- ambiguous_option::alternatives() const throw()
+
+
+
+ invalid_bool_value::
+ invalid_bool_value(const std::string& bad_value)
+ : validation_error(validation_error::invalid_bool_value)
     {
- return m_alternatives;
+ set_substitute("value", bad_value);
     }
 
- void
- multiple_values::set_option_name(const std::string& option_name)
+
+
+
+
+
+ error_with_option_name::error_with_option_name( const std::string& template_,
+ const std::string& option_name,
+ const std::string& original_token,
+ int option_style) :
+ error(template_),
+ m_option_style(option_style),
+ m_error_template(template_)
     {
- m_option_name = option_name;
+ // parameter | placeholder | value
+ // --------- | ----------- | -----
+ set_substitute_default("canonical_option", "option '%canonical_option%'", "option");
+ set_substitute_default("value", "argument ('%value%')", "argument");
+ set_substitute_default("prefix", "%prefix%", "");
+ m_substitutions["option"] = option_name;
+ m_substitutions["original_token"] = original_token;
     }
 
- const std::string&
- multiple_values::get_option_name() const throw()
+
+ const char* error_with_option_name::what() const throw()
     {
- return m_option_name;
+ // will substitute tokens each time what is run()
+ substitute_placeholders(m_error_template);
+
+ return m_message.c_str();
     }
-
- void
- multiple_occurrences::set_option_name(const std::string& option_name)
+
+ void error_with_option_name::replace_token(const string& from, const string& to) const
     {
- m_option_name = option_name;
+ while (1)
+ {
+ std::size_t pos = m_message.find(from.c_str(), 0, from.length());
+ // not found: all replaced
+ if (pos == std::string::npos)
+ return;
+ m_message.replace(pos, from.length(), to);
+ }
     }
 
- const std::string&
- multiple_occurrences::get_option_name() const throw()
+ string error_with_option_name::get_canonical_option_prefix() const
     {
- return m_option_name;
- }
-
- validation_error::
- validation_error(kind_t kind,
- const std::string& option_value,
- const std::string& option_name)
- : error("")
- , m_kind(kind)
- , m_option_name(option_name)
- , m_option_value(option_value)
- , m_message(error_message(kind))
- {
- if (!option_value.empty())
- {
- m_message.append(std::string("'") + option_value + std::string("'"));
- }
+ switch (m_option_style)
+ {
+ case command_line_style::allow_dash_for_short:
+ return "-";
+ case command_line_style::allow_slash_for_short:
+ return "/";
+ case command_line_style::allow_long_disguise:
+ return "-";
+ case command_line_style::allow_long:
+ return "--";
+ case 0:
+ return "";
+ }
+ throw std::logic_error("error_with_option_name::m_option_style can only be "
+ "one of [0, allow_dash_for_short, allow_slash_for_short, "
+ "allow_long_disguise or allow_long]");
     }
 
- void
- validation_error::set_option_name(const std::string& option_name)
+
+ string error_with_option_name::get_canonical_option_name() const
     {
- m_option_name = option_name;
+ if (!m_substitutions.find("option")->second.length())
+ return m_substitutions.find("original_token")->second;
+
+ string original_token = strip_prefixes(m_substitutions.find("original_token")->second);
+ string option_name = strip_prefixes(m_substitutions.find("option")->second);
+
+ // For long options, use option name
+ if (m_option_style == command_line_style::allow_long ||
+ m_option_style == command_line_style::allow_long_disguise)
+ return get_canonical_option_prefix() + option_name;
+
+ // For short options use first letter of original_token
+ if (m_option_style && original_token.length())
+ return get_canonical_option_prefix() + original_token[0];
+
+ // no prefix
+ return option_name;
     }
 
- const std::string&
- validation_error::get_option_name() const throw()
+
+ void error_with_option_name::substitute_placeholders(const string& error_template) const
     {
- return m_option_name;
+ m_message = error_template;
+ std::map<std::string, std::string> substitutions(m_substitutions);
+ substitutions["canonical_option"] = get_canonical_option_name();
+ substitutions["prefix"] = get_canonical_option_prefix();
+
+
+ //
+ // replace placeholder with defaults if values are missing
+ //
+ for (map<string, string_pair>::const_iterator iter = m_substitution_defaults.begin();
+ iter != m_substitution_defaults.end(); ++iter)
+ {
+ // missing parameter: use default
+ if (substitutions.count(iter->first) == 0 ||
+ substitutions[iter->first].length() == 0)
+ replace_token(iter->second.first, iter->second.second);
+ }
+
+
+ //
+ // replace placeholder with values
+ // placeholder are denoted by surrounding '%'
+ //
+ for (map<string, string>::iterator iter = substitutions.begin();
+ iter != substitutions.end(); ++iter)
+ replace_token('%' + iter->first + '%', iter->second);
     }
 
- std::string
- validation_error::error_message(kind_t kind)
+
+ void ambiguous_option::substitute_placeholders(const string& original_error_template) const
+ {
+ // For short forms, all alternatives must be identical, by
+ // definition, to the specified option, so we don't need to
+ // display alternatives
+ if (m_option_style == command_line_style::allow_dash_for_short ||
+ m_option_style == command_line_style::allow_slash_for_short)
+ {
+ error_with_option_name::substitute_placeholders(original_error_template);
+ return;
+ }
+
+
+ string error_template = original_error_template;
+ // remove duplicates using std::set
+ std::set<std::string> alternatives_set (m_alternatives.begin(), m_alternatives.end());
+ std::vector<std::string> alternatives_vec (alternatives_set.begin(), alternatives_set.end());
+
+ error_template += " and matches ";
+ // Being very cautious: should be > 1 alternative!
+ if (alternatives_vec.size() > 1)
+ {
+ for (unsigned i = 0; i < alternatives_vec.size() - 1; ++i)
+ error_template += "'%prefix%" + alternatives_vec[i] + "', ";
+ error_template += "and ";
+ }
+
+ // there is a programming error if multiple options have the same name...
+ if (m_alternatives.size() > 1 && alternatives_vec.size() == 1)
+ error_template += "different versions of ";
+
+ error_template += "'%prefix%" + alternatives_vec.back() + "'";
+
+
+ // use inherited logic
+ error_with_option_name::substitute_placeholders(error_template);
+ }
+
+
+
+
+
+
+ string
+ validation_error::get_template(kind_t kind)
     {
         // Initially, store the message in 'const char*' variable,
         // to avoid conversion to std::string in all cases.
         const char* msg;
         switch(kind)
         {
- case multiple_values_not_allowed:
- msg = "multiple values not allowed";
- break;
- case at_least_one_value_required:
- msg = "at least one value required";
- break;
         case invalid_bool_value:
- msg = "invalid bool value";
+ msg = "the argument ('%value%') for option '%canonical_option%' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'";
             break;
         case invalid_option_value:
- msg = "invalid option value";
+ msg = "the argument ('%value%') for option '%canonical_option%' is invalid";
+ break;
+ case multiple_values_not_allowed:
+ msg = "option '%canonical_option%' only takes a single argument";
+ break;
+ case at_least_one_value_required:
+ msg = "option '%canonical_option%' requires at least one argument";
             break;
+ // currently unused
         case invalid_option:
- msg = "invalid option";
+ msg = "option '%canonical_option%' is not valid";
             break;
         default:
             msg = "unknown error";
@@ -315,21 +425,4 @@
         return msg;
     }
 
- const char*
- validation_error::what() const throw()
- {
- if (!m_option_name.empty())
- {
- m_message = "in option '" + m_option_name + "': "
- + error_message(m_kind);
- }
- 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 2012-04-08 04:42:39 EDT (Sun, 08 Apr 2012)
@@ -38,65 +38,68 @@
         // Declared once, to please Intel in VC++ mode;
         unsigned i;
 
- // First, convert/store all given options
- for (i = 0; i < options.options.size(); ++i) {
+ // Declared here so can be used to provide context for exceptions
+ string option_name;
+ string original_token;
 
- const string& name = options.options[i].string_key;
- // Skip positional options without name
- if (name.empty())
- continue;
-
- // Ignore unregistered option. The 'unregistered'
- // field can be true only if user has explicitly asked
- // to allow unregistered options. We can't store them
- // to variables map (lacking any information about paring),
- // so just ignore them.
- if (options.options[i].unregistered)
- continue;
-
- // If option has final value, skip this assignment
- if (xm.m_final.count(name))
- continue;
+ try
+ {
 
- const option_description& d = desc.find(name, false,
- false, false);
+ // First, convert/store all given options
+ for (i = 0; i < options.options.size(); ++i) {
 
- variable_value& v = m[name];
- if (v.defaulted()) {
- // Explicit assignment here erases defaulted value
- v = variable_value();
- }
-
- try {
+ option_name = options.options[i].string_key;
+ original_token = options.options[i].original_tokens.size() ?
+ options.options[i].original_tokens[0] :
+ option_name;
+ // Skip positional options without name
+ if (option_name.empty())
+ continue;
+
+ // Ignore unregistered option. The 'unregistered'
+ // field can be true only if user has explicitly asked
+ // to allow unregistered options. We can't store them
+ // to variables map (lacking any information about paring),
+ // so just ignore them.
+ if (options.options[i].unregistered)
+ continue;
+
+ // If option has final value, skip this assignment
+ if (xm.m_final.count(option_name))
+ continue;
+
+ string original_token = options.options[i].original_tokens.size() ?
+ options.options[i].original_tokens[0] : "";
+ const option_description& d = desc.find(option_name, false,
+ false, false);
+
+ variable_value& v = m[option_name];
+ if (v.defaulted()) {
+ // Explicit assignment here erases defaulted value
+ v = variable_value();
+ }
+
                 d.semantic()->parse(v.value(), options.options[i].value, utf8);
+
+ v.m_value_semantic = d.semantic();
+
+ // The option is not composing, and the value is explicitly
+ // provided. Ignore values of this option for subsequent
+ // calls to 'store'. We store this to a temporary set,
+ // so that several assignment inside *this* 'store' call
+ // are allowed.
+ if (!d.semantic()->is_composing())
+ new_final.insert(option_name);
             }
+ }
 #ifndef BOOST_NO_EXCEPTIONS
- catch(validation_error& e)
- {
- e.set_option_name(name);
- throw;
- }
- catch(multiple_occurrences& e)
- {
- e.set_option_name(name);
- throw;
- }
- catch(multiple_values& e)
- {
- e.set_option_name(name);
- throw;
- }
-#endif
- v.m_value_semantic = d.semantic();
-
- // The option is not composing, and the value is explicitly
- // provided. Ignore values of this option for subsequent
- // calls to 'store'. We store this to a temporary set,
- // so that several assignment inside *this* 'store' call
- // are allowed.
- if (!d.semantic()->is_composing())
- new_final.insert(name);
+ catch(error_with_option_name& e)
+ {
+ // add context and rethrow
+ e.add_context(option_name, original_token, options.m_options_prefix);
+ throw;
         }
+#endif
         xm.m_final.insert(new_final.begin(), new_final.end());
 
         
@@ -127,7 +130,14 @@
 
             // add empty value if this is an required option
             if (d.semantic()->is_required()) {
- xm.m_required.insert(key);
+
+ // For option names specified in multiple ways, e.g. on the command line,
+ // config file etc, the following precedence rules apply:
+ // "--" > ("-" or "/") > ""
+ // Precedence is set conveniently by a single call to length()
+ string canonical_name = d.canonical_display_name(options.m_options_prefix);
+ if (canonical_name.length() > xm.m_required[key].length())
+ xm.m_required[key] = canonical_name;
             }
         }
     }
@@ -204,15 +214,16 @@
     variables_map::notify()
     {
         // This checks if all required options occur
- for (set<string>::const_iterator r = m_required.begin();
+ for (map<string, string>::const_iterator r = m_required.begin();
              r != m_required.end();
              ++r)
         {
- const string& opt = *r;
+ const string& opt = r->first;
+ const string& display_opt = r->second;
             map<string, variable_value>::const_iterator iter = find(opt);
             if (iter == end() || iter->second.empty())
             {
- boost::throw_exception(required_option(opt));
+ boost::throw_exception(required_option(display_opt));
             
             }
         }

Modified: trunk/libs/program_options/test/exception_test.cpp
==============================================================================
--- trunk/libs/program_options/test/exception_test.cpp (original)
+++ trunk/libs/program_options/test/exception_test.cpp 2012-04-08 04:42:39 EDT (Sun, 08 Apr 2012)
@@ -63,7 +63,7 @@
    catch (unknown_option& e)
    {
       BOOST_CHECK_EQUAL(e.get_option_name(), "-f");
- BOOST_CHECK_EQUAL(string(e.what()), "unknown option -f");
+ BOOST_CHECK_EQUAL(string(e.what()), "unrecognised option '-f'");
    }
 }
 
@@ -94,8 +94,8 @@
       // because: untyped_value always has one value and this is filtered before reach specific
       // validation and parsing
       //
- BOOST_CHECK_EQUAL(e.get_option_name(), "cfgfile");
- BOOST_CHECK_EQUAL(string(e.what()), "in option 'cfgfile': multiple values not allowed");
+ BOOST_CHECK_EQUAL(e.get_option_name(), "--cfgfile");
+ BOOST_CHECK_EQUAL(string(e.what()), "option '--cfgfile' only takes a single argument");
    }
 }
 
@@ -118,8 +118,8 @@
    }
    catch (multiple_occurrences& e)
    {
- BOOST_CHECK_EQUAL(e.get_option_name(), "cfgfile");
- BOOST_CHECK_EQUAL(string(e.what()), "multiple occurrences");
+ BOOST_CHECK_EQUAL(e.get_option_name(), "--cfgfile");
+ BOOST_CHECK_EQUAL(string(e.what()), "option '--cfgfile' cannot be specified more than once");
    }
 }
 
@@ -145,7 +145,7 @@
    catch (invalid_command_line_syntax& e)
    {
       BOOST_CHECK_EQUAL(e.kind(), invalid_syntax::missing_parameter);
- BOOST_CHECK_EQUAL(e.tokens(), "cfgfile");
+ BOOST_CHECK_EQUAL(e.tokens(), "--cfgfile");
    }
 }
 

Added: trunk/libs/program_options/test/exception_txt_test.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/program_options/test/exception_txt_test.cpp 2012-04-08 04:42:39 EDT (Sun, 08 Apr 2012)
@@ -0,0 +1,4053 @@
+// Copyright Leo Goodstadt 2012
+// 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/parsers.hpp>
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/cmdline.hpp>
+using namespace boost::program_options;
+
+#include <iostream>
+#include <sstream>
+#include <vector>
+#include <cassert>
+using namespace std;
+
+#include "minitest.hpp"
+
+
+
+//
+// like BOOST_CHECK_EQUAL but with more descriptive error message
+//
+#define CHECK_EQUAL(description, a, b) if (a != b) {std::cerr << "\n\nError:\n<<" << \
+ description << ">>\n Expected text=\"" << b << "\"\n Actual text =\"" << a << "\"\n\n"; assert(a == b);}
+
+
+// Uncomment for Debugging, removes asserts so we can see more failures!
+//#define BOOST_ERROR(description) std::cerr << description; std::cerr << "\n";
+
+
+//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
+//
+// Uncomment to print out the complete set of diagnostic messages for the different test cases
+/*
+#define CHECK_EQUAL(description, a, b) if (a != b) {std::cerr << "\n\nError: " << \
+ description << "\n Expecting\n" << b << "\n Found\n" << a << "\n\n"; } \
+ else {std::cout << description<< "\t" << b << "\n";}
+*/
+
+//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
+
+
+//
+// test exception for each specified command line style, e.g. short dash or config file
+//
+template<typename EXCEPTION>
+void test_each_exception_message(const string& test_description, const vector<const char*>& argv, options_description& desc, int style, string exception_msg, istream& is = cin)
+{
+ if (exception_msg.length() == 0)
+ return;
+ variables_map vm;
+ unsigned argc = argv.size();
+
+
+ try {
+ if (style == -1)
+ store(parse_config_file(is, desc), vm);
+ else
+ store(parse_command_line(argv.size(), argv.data(), desc, style), vm);
+ notify(vm);
+ }
+ catch (EXCEPTION& e)
+ {
+ //cerr << "Correct:\n\t" << e.what() << "\n";
+ CHECK_EQUAL(test_description, e.what(), exception_msg);
+ return;
+ }
+ catch (std::exception& e)
+ {
+ // concatenate argv without boost::algorithm::join
+ string argv_txt;
+ for (unsigned ii = 0; ii < argc - 1; ++ii)
+ argv_txt += argv[ii] + string(" ");
+ if (argc)
+ argv_txt += argv[argc - 1];
+
+ BOOST_ERROR("\n<<" + test_description +
+ string(">>\n Unexpected exception type!\n Actual text =\"") + e.what() +
+ "\"\n argv =\"" + argv_txt +
+ "\"\n Expected text=\"" + exception_msg + "\"\n");
+ return;
+ }
+ BOOST_ERROR(test_description + ": No exception thrown. ");
+}
+
+
+
+
+//
+// test exception messages for all command line styles (unix/long/short/slash/config file)
+//
+// try each command line style in turn
+const int unix_style = command_line_style::unix_style;
+const int short_dash = command_line_style::allow_dash_for_short | command_line_style::allow_short | command_line_style::short_allow_adjacent | command_line_style::allow_sticky;
+const int short_slash = command_line_style::allow_slash_for_short | command_line_style::allow_short | command_line_style::short_allow_adjacent;
+const int long_dash = command_line_style::allow_long | command_line_style::long_allow_adjacent | command_line_style::allow_guessing;
+
+
+
+template<typename EXCEPTION>
+void test_exception_message(const vector<vector<const char*>>& argv,
+ options_description& desc,
+ const string& error_description,
+ const char* expected_message_template[5])
+{
+ string expected_message;
+
+ // unix
+ expected_message = expected_message_template[0];
+ test_each_exception_message<EXCEPTION>(error_description + " -- unix",
+ argv[0], desc, unix_style, expected_message);
+
+ // long dash only
+ expected_message = expected_message_template[1];
+ test_each_exception_message<EXCEPTION>(error_description + " -- long_dash",
+ argv[1], desc, long_dash, expected_message);
+
+
+ // short dash only
+ expected_message = expected_message_template[2];
+ test_each_exception_message<EXCEPTION>(error_description + " -- short_dash",
+ argv[2], desc, short_dash, expected_message);
+
+ // short slash only
+ expected_message = expected_message_template[3];
+ test_each_exception_message<EXCEPTION>(error_description + " -- short_slash",
+ argv[3], desc, short_slash, expected_message);
+
+ // config file only
+ expected_message = expected_message_template[4];
+ if (expected_message.length())
+ {
+ istringstream istrm(argv[4][0]);
+ test_each_exception_message<EXCEPTION>(error_description + " -- config_file",
+ argv[4], desc, -1, expected_message, istrm);
+ }
+
+}
+
+#define VEC_STR_PUSH_BACK(vec, c_array) \
+ vec.push_back(vector<const char*>(c_array, c_array + sizeof(c_array) / sizeof(char*)));
+
+//________________________________________________________________________________________
+//
+// invalid_option_value
+//
+//________________________________________________________________________________________
+void test_invalid_option_value_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("int_option,d", value< int >(), "An option taking an integer")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--int", "A_STRING"}; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "int_option=A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv4);
+
+ const char* expected_msg[5] = {
+ "the argument ('A_STRING') for option '--int_option' is invalid",
+ "the argument ('A_STRING') for option '--int_option' is invalid",
+ "the argument ('A_STRING') for option '-d' is invalid",
+ "the argument ('A_STRING') for option '/d' is invalid",
+ "the argument ('A_STRING') for option 'int_option' is invalid"
+ };
+
+
+ test_exception_message<invalid_option_value>(argv, desc, "invalid_option_value",
+ expected_msg);
+
+
+}
+
+//________________________________________________________________________________________
+//
+// missing_value
+//
+//________________________________________________________________________________________
+void test_missing_value_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,e", value<string>(), "the config file")
+ ("output,o", value<string>(), "the output file")
+ ;
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-e", "-e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--cfgfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-e", "-e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/e", "/e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { ""} ; VEC_STR_PUSH_BACK(argv, argv4);
+
+ const char* expected_msg[5] = {
+ "the required argument for option '--cfgfile' is missing",
+ "the required argument for option '--cfgfile' is missing",
+ "the required argument for option '-e' is missing",
+ "", // Ignore probable bug in cmdline::finish_option
+ //"the required argument for option '/e' is missing",
+ "",
+ };
+ test_exception_message<invalid_command_line_syntax>(argv, desc,
+ "invalid_syntax::missing_parameter",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// ambiguous_option
+//
+//________________________________________________________________________________________
+void test_ambiguous_option_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile1,c", value<string>(), "the config file")
+ ("cfgfile2,o", value<string>(), "the config file")
+ ("good,g", "good option")
+ ("output,c", value<string>(), "the output file")
+ ("output", value<string>(), "the output file")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-ggc", "file", "-o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--cfgfile", "file", "--cfgfile", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-ggc", "file", "-o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/c", "file", "/o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "output=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '-c' is ambiguous and matches '--cfgfile1', and '--output'",
+ "option '--cfgfile' is ambiguous and matches '--cfgfile1', and '--cfgfile2'",
+ "option '-c' is ambiguous",
+ "option '/c' is ambiguous",
+ "option 'output' is ambiguous and matches different versions of 'output'",
+ };
+ test_exception_message<ambiguous_option>(argv, desc, "ambiguous_option",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// multiple_occurrences
+//
+//________________________________________________________________________________________
+void test_multiple_occurrences_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<string>(), "the configfile")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-c", "file", "-c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--cfgfi", "file", "--cfgfi", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-c", "file", "-c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/c", "file", "/c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "cfgfile=output.txt\ncfgfile=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '--cfgfile' cannot be specified more than once",
+ "option '--cfgfile' cannot be specified more than once",
+ "option '-c' cannot be specified more than once",
+ "option '/c' cannot be specified more than once",
+ "option 'cfgfile' cannot be specified more than once",
+ };
+ test_exception_message<multiple_occurrences>(argv, desc, "multiple_occurrences",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// unknown_option
+//
+//________________________________________________________________________________________
+void test_unknown_option_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("good,g", "good option")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-ggc", "file"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--cfgfile", "file"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-ggc", "file"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/c", "file"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "cfgfile=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "unrecognised option '-ggc'",
+ "unrecognised option '--cfgfile'",
+ "unrecognised option '-ggc'",
+ "unrecognised option '/c'",
+ "unrecognised option 'cfgfile'",
+ };
+ test_exception_message<unknown_option>(argv, desc, "unknown_option", expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// validation_error::invalid_bool_value
+//
+//________________________________________________________________________________________
+void test_invalid_bool_value_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("bool_option,b", value< bool>(), "bool_option")
+ ;
+
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-b", "file"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--bool_optio", "file"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-b", "file"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/b", "file"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "bool_option=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "the argument ('file') for option '--bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('file') for option '--bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('file') for option '-b' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('file') for option '/b' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('output.txt') for option 'bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ };
+ test_exception_message<validation_error>(argv,
+ desc,
+ "validation_error::invalid_bool_value",
+ expected_msg);
+}
+
+
+
+
+//________________________________________________________________________________________
+//
+// validation_error::multiple_values_not_allowed
+//
+//________________________________________________________________________________________
+//
+// Strange exception: sole purpose seems to be catching multitoken() associated with a scalar
+// validation_error::multiple_values_not_allowed seems thus to be a programmer error
+//
+//
+void test_multiple_values_not_allowed_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<string>()->multitoken(), "the config file")
+ ("good,g", "good option")
+ ("output,o", value<string>(), "the output file")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-c", "file", "c", "-o", "fritz", "hugo" } ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--cfgfil", "file", "c", "--outpu", "fritz", "hugo" } ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-c", "file", "c", "-o", "fritz", "hugo"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/c", "file", "c", "/o", "fritz", "hugo"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '--cfgfile' only takes a single argument",
+ "option '--cfgfile' only takes a single argument",
+ "option '-c' only takes a single argument",
+ "option '/c' only takes a single argument",
+ "",
+ };
+ test_exception_message<validation_error>(argv,
+ desc,
+ "validation_error::multiple_values_not_allowed",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// validation_error::at_least_one_value_required
+//
+//________________________________________________________________________________________
+//
+// Strange exception: sole purpose seems to be catching zero_tokens() associated with a scalar
+// validation_error::multiple_values_not_allowed seems thus to be a programmer error
+//
+//
+void test_at_least_one_value_required_exception_msg()
+{
+
+
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<int>()->zero_tokens(), "the config file")
+ ("other,o", value<string>(), "other")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-c" } ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--cfg", "--o", "name" } ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-c" , "-o" , "name" } ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/c" } ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '--cfgfile' requires at least one argument",
+ "option '--cfgfile' requires at least one argument",
+ "option '-c' requires at least one argument",
+ "option '/c' requires at least one argument",
+ "",
+ };
+ test_exception_message<validation_error>(argv,
+ desc,
+ "validation_error::at_least_one_value_required",
+ expected_msg);
+}
+
+
+//________________________________________________________________________________________
+//
+// required_option
+//
+//________________________________________________________________________________________
+void test_required_option_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<string>()->required(), "the config file")
+ ("good,g", "good option")
+ ("output,o", value<string>()->required(), "the output file")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-g" } ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--g" } ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-g"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/g"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "the option '--cfgfile' is required but missing",
+ "the option '--cfgfile' is required but missing",
+ "the option '-c' is required but missing",
+ "the option '/c' is required but missing",
+ "the option 'cfgfile' is required but missing",
+ };
+ test_exception_message<required_option>(argv,
+ desc,
+ "required_option",
+ expected_msg);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**
+ * Check if this is the expected exception with the right message is being thrown inside
+ * func
+*/
+template <typename EXCEPTION, typename FUNC>
+void test_exception(const string& test_name, const string& exception_txt, FUNC func)
+{
+
+ try {
+ options_description desc;
+ variables_map vm;
+ func(desc, vm);
+ }
+ catch (EXCEPTION& e)
+ {
+ CHECK_EQUAL(test_name, e.what(), exception_txt);
+ return;
+ }
+ catch (std::exception& e)
+ {
+ BOOST_ERROR(string(test_name + ":\nUnexpected exception. ") + e.what() +
+ "\nExpected text:\n" + exception_txt + "\n\n");
+ return;
+ }
+ BOOST_ERROR(test_name + ": No exception thrown. ");
+}
+
+
+
+//________________________________________________________________________________________
+//
+// check_reading_file
+//
+//________________________________________________________________________________________
+void check_reading_file(options_description& desc, variables_map& vm)
+{
+ desc.add_options()
+ ("output,o", value<string>(), "the output file");
+
+ const char* file_name = "no_such_file";
+ store(parse_config_file<char>(file_name, desc, true), vm);
+
+}
+
+
+//________________________________________________________________________________________
+//
+// config_file_wildcard
+//
+//________________________________________________________________________________________
+void config_file_wildcard(options_description& desc, variables_map& vm)
+{
+ desc.add_options()
+ ("outpu*", value<string>(), "the output file1")
+ ("outp*", value<string>(), "the output file2")
+ ;
+ istringstream is("output1=whichone\noutput2=whichone\n");
+ store(parse_config_file(is, desc), vm);
+}
+
+//________________________________________________________________________________________
+//
+// invalid_syntax::unrecognized_line
+//
+//________________________________________________________________________________________
+void unrecognized_line(options_description& desc, variables_map& vm)
+{
+ istringstream is("funny wierd line\n");
+ store(parse_config_file(is, desc), vm);
+}
+
+//________________________________________________________________________________________
+//
+// abbreviated_options_in_config_file
+//
+//________________________________________________________________________________________
+void abbreviated_options_in_config_file(options_description& desc, variables_map& vm)
+{
+ desc.add_options()(",o", value<string>(), "the output file");
+ istringstream is("o=output.txt\n");
+ store(parse_config_file(is, desc), vm);
+}
+
+
+//________________________________________________________________________________________
+//
+// too_many_positional_options
+//
+//________________________________________________________________________________________
+void too_many_positional_options(options_description& desc, variables_map& vm)
+{
+ const char* argv[] = {"program", "1", "2", "3"};
+ positional_options_description positional_args;
+ positional_args.add("two_positional_arguments", 2);
+ store(command_line_parser(4, argv).options(desc).positional(positional_args).run(), vm);
+}
+
+
+//________________________________________________________________________________________
+//
+// invalid_command_line_style
+//
+//________________________________________________________________________________________
+
+void test_invalid_command_line_style_exception_msg()
+{
+ string test_name = "invalid_command_line_style";
+ using namespace command_line_style;
+ options_description desc;
+ desc.add_options()("output,o", value<string>(), "the output file");
+
+ vector<int> invalid_styles;
+ invalid_styles.push_back(allow_short | short_allow_adjacent);
+ invalid_styles.push_back(allow_short | allow_dash_for_short);
+ invalid_styles.push_back(allow_long);
+ vector<string> invalid_diagnostics;
+ invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
+ "or other of 'command_line_style::allow_slash_for_short' "
+ "(slashes) or 'command_line_style::allow_dash_for_short' "
+ "(dashes) for short options.");
+ invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
+ "or other of 'command_line_style::short_allow_next' "
+ "(whitespace separated arguments) or "
+ "'command_line_style::short_allow_adjacent' ('=' "
+ "separated arguments) for short options.");
+ invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
+ "or other of 'command_line_style::long_allow_next' "
+ "(whitespace separated arguments) or "
+ "'command_line_style::long_allow_adjacent' ('=' "
+ "separated arguments) for long options.");
+
+
+ const char* argv[] = {"program"};
+ variables_map vm;
+ for (unsigned ii = 0; ii < 3; ++ii)
+ {
+ bool exception_thrown = false;
+ try
+ {
+ store(parse_command_line(1, argv, desc, invalid_styles[ii]), vm);
+ }
+ catch (invalid_command_line_style& e)
+ {
+ string error_msg("arguments are not allowed for unabbreviated option names");
+ CHECK_EQUAL(test_name, e.what(), invalid_diagnostics[ii]);
+ exception_thrown = true;
+ }
+ catch (std::exception& e)
+ {
+ BOOST_ERROR(string(test_name + ":\nUnexpected exception. ") + e.what() +
+ "\nExpected text:\n" + invalid_diagnostics[ii] + "\n");
+ exception_thrown = true;
+ }
+ if (!exception_thrown)
+ {
+ BOOST_ERROR(test_name << ": No exception thrown. ");
+ }
+ }
+}
+
+
+
+int main(int /*ac*/, char** /*av*/)
+{
+ test_ambiguous_option_exception_msg();
+ test_unknown_option_exception_msg();
+ test_multiple_occurrences_exception_msg();
+ test_missing_value_exception_msg();
+ test_invalid_option_value_exception_msg();
+ test_invalid_bool_value_exception_msg();
+ test_multiple_values_not_allowed_exception_msg();
+ test_required_option_exception_msg();
+ test_at_least_one_value_required_exception_msg();
+
+ string test_name;
+ string expected_message;
+
+
+ // check_reading_file
+ test_name = "check_reading_file";
+ expected_message = "can not read options configuration file 'no_such_file'";
+ test_exception<reading_file>(test_name, expected_message, check_reading_file);
+
+ // config_file_wildcard
+ test_name = "config_file_wildcard";
+ expected_message = "options 'outpu*' and 'outp*' will both match the same arguments from the configuration file";
+ test_exception<error>(test_name, expected_message, config_file_wildcard);
+
+ // unrecognized_line
+ test_name = "unrecognized_line";
+ expected_message = "the options configuration file contains an invalid line 'funny wierd line'";
+ test_exception<invalid_syntax>(test_name, expected_message, unrecognized_line);
+
+
+ // abbreviated_options_in_config_file
+ test_name = "abbreviated_options_in_config_file";
+ expected_message = "abbreviated option names are not permitted in options configuration files";
+ test_exception<error>(test_name, expected_message, abbreviated_options_in_config_file);
+
+ test_name = "too_many_positional_options";
+ expected_message = "too many positional arguments have been specified on the command line";
+ test_exception<too_many_positional_options_error>(
+ test_name, expected_message, too_many_positional_options);
+
+ test_invalid_command_line_style_exception_msg();
+
+
+ return 0;
+}
+
+
+
+// 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/parsers.hpp>
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/cmdline.hpp>
+using namespace boost::program_options;
+
+#include <iostream>
+#include <sstream>
+#include <vector>
+#include <cassert>
+using namespace std;
+
+#include "minitest.hpp"
+
+
+
+//
+// like BOOST_CHECK_EQUAL but with more descriptive error message
+//
+#define CHECK_EQUAL(description, a, b) if (a != b) {std::cerr << "\n\nError:\n<<" << \
+ description << ">>\n Expected text=\"" << b << "\"\n Actual text =\"" << a << "\"\n\n"; assert(a == b);}
+
+
+// Uncomment for Debugging, removes asserts so we can see more failures!
+//#define BOOST_ERROR(description) std::cerr << description; std::cerr << "\n";
+
+
+//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
+//
+// Uncomment to print out the complete set of diagnostic messages for the different test cases
+/*
+#define CHECK_EQUAL(description, a, b) if (a != b) {std::cerr << "\n\nError: " << \
+ description << "\n Expecting\n" << b << "\n Found\n" << a << "\n\n"; } \
+ else {std::cout << description<< "\t" << b << "\n";}
+*/
+
+//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
+
+
+//
+// test exception for each specified command line style, e.g. short dash or config file
+//
+template<typename EXCEPTION>
+void test_each_exception_message(const string& test_description, const vector<const char*>& argv, options_description& desc, int style, string exception_msg, istream& is = cin)
+{
+ if (exception_msg.length() == 0)
+ return;
+ variables_map vm;
+ unsigned argc = argv.size();
+
+
+ try {
+ if (style == -1)
+ store(parse_config_file(is, desc), vm);
+ else
+ store(parse_command_line(argv.size(), argv.data(), desc, style), vm);
+ notify(vm);
+ }
+ catch (EXCEPTION& e)
+ {
+ //cerr << "Correct:\n\t" << e.what() << "\n";
+ CHECK_EQUAL(test_description, e.what(), exception_msg);
+ return;
+ }
+ catch (std::exception& e)
+ {
+ // concatenate argv without boost::algorithm::join
+ string argv_txt;
+ for (unsigned ii = 0; ii < argc - 1; ++ii)
+ argv_txt += argv[ii] + string(" ");
+ if (argc)
+ argv_txt += argv[argc - 1];
+
+ BOOST_ERROR("\n<<" + test_description +
+ string(">>\n Unexpected exception type!\n Actual text =\"") + e.what() +
+ "\"\n argv =\"" + argv_txt +
+ "\"\n Expected text=\"" + exception_msg + "\"\n");
+ return;
+ }
+ BOOST_ERROR(test_description + ": No exception thrown. ");
+}
+
+
+
+
+//
+// test exception messages for all command line styles (unix/long/short/slash/config file)
+//
+// try each command line style in turn
+const int unix_style = command_line_style::unix_style;
+const int short_dash = command_line_style::allow_dash_for_short | command_line_style::allow_short | command_line_style::short_allow_adjacent | command_line_style::allow_sticky;
+const int short_slash = command_line_style::allow_slash_for_short | command_line_style::allow_short | command_line_style::short_allow_adjacent;
+const int long_dash = command_line_style::allow_long | command_line_style::long_allow_adjacent | command_line_style::allow_guessing;
+
+
+
+template<typename EXCEPTION>
+void test_exception_message(const vector<vector<const char*>>& argv,
+ options_description& desc,
+ const string& error_description,
+ const char* expected_message_template[5])
+{
+ string expected_message;
+
+ // unix
+ expected_message = expected_message_template[0];
+ test_each_exception_message<EXCEPTION>(error_description + " -- unix",
+ argv[0], desc, unix_style, expected_message);
+
+ // long dash only
+ expected_message = expected_message_template[1];
+ test_each_exception_message<EXCEPTION>(error_description + " -- long_dash",
+ argv[1], desc, long_dash, expected_message);
+
+
+ // short dash only
+ expected_message = expected_message_template[2];
+ test_each_exception_message<EXCEPTION>(error_description + " -- short_dash",
+ argv[2], desc, short_dash, expected_message);
+
+ // short slash only
+ expected_message = expected_message_template[3];
+ test_each_exception_message<EXCEPTION>(error_description + " -- short_slash",
+ argv[3], desc, short_slash, expected_message);
+
+ // config file only
+ expected_message = expected_message_template[4];
+ if (expected_message.length())
+ {
+ istringstream istrm(argv[4][0]);
+ test_each_exception_message<EXCEPTION>(error_description + " -- config_file",
+ argv[4], desc, -1, expected_message, istrm);
+ }
+
+}
+
+#define VEC_STR_PUSH_BACK(vec, c_array) \
+ vec.push_back(vector<const char*>(c_array, c_array + sizeof(c_array) / sizeof(char*)));
+
+//________________________________________________________________________________________
+//
+// invalid_option_value
+//
+//________________________________________________________________________________________
+void test_invalid_option_value_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("int_option,d", value< int >(), "An option taking an integer")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--int", "A_STRING"}; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "int_option=A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv4);
+
+ const char* expected_msg[5] = {
+ "the argument ('A_STRING') for option '--int_option' is invalid",
+ "the argument ('A_STRING') for option '--int_option' is invalid",
+ "the argument ('A_STRING') for option '-d' is invalid",
+ "the argument ('A_STRING') for option '/d' is invalid",
+ "the argument ('A_STRING') for option 'int_option' is invalid"
+ };
+
+
+ test_exception_message<invalid_option_value>(argv, desc, "invalid_option_value",
+ expected_msg);
+
+
+}
+
+//________________________________________________________________________________________
+//
+// missing_value
+//
+//________________________________________________________________________________________
+void test_missing_value_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,e", value<string>(), "the config file")
+ ("output,o", value<string>(), "the output file")
+ ;
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-e", "-e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--cfgfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-e", "-e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/e", "/e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { ""} ; VEC_STR_PUSH_BACK(argv, argv4);
+
+ const char* expected_msg[5] = {
+ "the required argument for option '--cfgfile' is missing",
+ "the required argument for option '--cfgfile' is missing",
+ "the required argument for option '-e' is missing",
+ "", // Ignore probable bug in cmdline::finish_option
+ //"the required argument for option '/e' is missing",
+ "",
+ };
+ test_exception_message<invalid_command_line_syntax>(argv, desc,
+ "invalid_syntax::missing_parameter",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// ambiguous_option
+//
+//________________________________________________________________________________________
+void test_ambiguous_option_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile1,c", value<string>(), "the config file")
+ ("cfgfile2,o", value<string>(), "the config file")
+ ("good,g", "good option")
+ ("output,c", value<string>(), "the output file")
+ ("output", value<string>(), "the output file")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-ggc", "file", "-o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--cfgfile", "file", "--cfgfile", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-ggc", "file", "-o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/c", "file", "/o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "output=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '-c' is ambiguous and matches '--cfgfile1', and '--output'",
+ "option '--cfgfile' is ambiguous and matches '--cfgfile1', and '--cfgfile2'",
+ "option '-c' is ambiguous",
+ "option '/c' is ambiguous",
+ "option 'output' is ambiguous and matches different versions of 'output'",
+ };
+ test_exception_message<ambiguous_option>(argv, desc, "ambiguous_option",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// multiple_occurrences
+//
+//________________________________________________________________________________________
+void test_multiple_occurrences_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<string>(), "the configfile")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-c", "file", "-c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--cfgfi", "file", "--cfgfi", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-c", "file", "-c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/c", "file", "/c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "cfgfile=output.txt\ncfgfile=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '--cfgfile' cannot be specified more than once",
+ "option '--cfgfile' cannot be specified more than once",
+ "option '-c' cannot be specified more than once",
+ "option '/c' cannot be specified more than once",
+ "option 'cfgfile' cannot be specified more than once",
+ };
+ test_exception_message<multiple_occurrences>(argv, desc, "multiple_occurrences",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// unknown_option
+//
+//________________________________________________________________________________________
+void test_unknown_option_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("good,g", "good option")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-ggc", "file"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--cfgfile", "file"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-ggc", "file"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/c", "file"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "cfgfile=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "unrecognised option '-ggc'",
+ "unrecognised option '--cfgfile'",
+ "unrecognised option '-ggc'",
+ "unrecognised option '/c'",
+ "unrecognised option 'cfgfile'",
+ };
+ test_exception_message<unknown_option>(argv, desc, "unknown_option", expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// validation_error::invalid_bool_value
+//
+//________________________________________________________________________________________
+void test_invalid_bool_value_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("bool_option,b", value< bool>(), "bool_option")
+ ;
+
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-b", "file"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--bool_optio", "file"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-b", "file"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/b", "file"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "bool_option=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "the argument ('file') for option '--bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('file') for option '--bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('file') for option '-b' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('file') for option '/b' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('output.txt') for option 'bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ };
+ test_exception_message<validation_error>(argv,
+ desc,
+ "validation_error::invalid_bool_value",
+ expected_msg);
+}
+
+
+
+
+//________________________________________________________________________________________
+//
+// validation_error::multiple_values_not_allowed
+//
+//________________________________________________________________________________________
+//
+// Strange exception: sole purpose seems to be catching multitoken() associated with a scalar
+// validation_error::multiple_values_not_allowed seems thus to be a programmer error
+//
+//
+void test_multiple_values_not_allowed_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<string>()->multitoken(), "the config file")
+ ("good,g", "good option")
+ ("output,o", value<string>(), "the output file")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-c", "file", "c", "-o", "fritz", "hugo" } ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--cfgfil", "file", "c", "--outpu", "fritz", "hugo" } ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-c", "file", "c", "-o", "fritz", "hugo"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/c", "file", "c", "/o", "fritz", "hugo"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '--cfgfile' only takes a single argument",
+ "option '--cfgfile' only takes a single argument",
+ "option '-c' only takes a single argument",
+ "option '/c' only takes a single argument",
+ "",
+ };
+ test_exception_message<validation_error>(argv,
+ desc,
+ "validation_error::multiple_values_not_allowed",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// validation_error::at_least_one_value_required
+//
+//________________________________________________________________________________________
+//
+// Strange exception: sole purpose seems to be catching zero_tokens() associated with a scalar
+// validation_error::multiple_values_not_allowed seems thus to be a programmer error
+//
+//
+void test_at_least_one_value_required_exception_msg()
+{
+
+
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<int>()->zero_tokens(), "the config file")
+ ("other,o", value<string>(), "other")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-c" } ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--cfg", "--o", "name" } ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-c" , "-o" , "name" } ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/c" } ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '--cfgfile' requires at least one argument",
+ "option '--cfgfile' requires at least one argument",
+ "option '-c' requires at least one argument",
+ "option '/c' requires at least one argument",
+ "",
+ };
+ test_exception_message<validation_error>(argv,
+ desc,
+ "validation_error::at_least_one_value_required",
+ expected_msg);
+}
+
+
+//________________________________________________________________________________________
+//
+// required_option
+//
+//________________________________________________________________________________________
+void test_required_option_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<string>()->required(), "the config file")
+ ("good,g", "good option")
+ ("output,o", value<string>()->required(), "the output file")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-g" } ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--g" } ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-g"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/g"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "the option '--cfgfile' is required but missing",
+ "the option '--cfgfile' is required but missing",
+ "the option '-c' is required but missing",
+ "the option '/c' is required but missing",
+ "the option 'cfgfile' is required but missing",
+ };
+ test_exception_message<required_option>(argv,
+ desc,
+ "required_option",
+ expected_msg);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**
+ * Check if this is the expected exception with the right message is being thrown inside
+ * func
+*/
+template <typename EXCEPTION, typename FUNC>
+void test_exception(const string& test_name, const string& exception_txt, FUNC func)
+{
+
+ try {
+ options_description desc;
+ variables_map vm;
+ func(desc, vm);
+ }
+ catch (EXCEPTION& e)
+ {
+ CHECK_EQUAL(test_name, e.what(), exception_txt);
+ return;
+ }
+ catch (std::exception& e)
+ {
+ BOOST_ERROR(string(test_name + ":\nUnexpected exception. ") + e.what() +
+ "\nExpected text:\n" + exception_txt + "\n\n");
+ return;
+ }
+ BOOST_ERROR(test_name + ": No exception thrown. ");
+}
+
+
+
+//________________________________________________________________________________________
+//
+// check_reading_file
+//
+//________________________________________________________________________________________
+void check_reading_file(options_description& desc, variables_map& vm)
+{
+ desc.add_options()
+ ("output,o", value<string>(), "the output file");
+
+ const char* file_name = "no_such_file";
+ store(parse_config_file<char>(file_name, desc, true), vm);
+
+}
+
+
+//________________________________________________________________________________________
+//
+// config_file_wildcard
+//
+//________________________________________________________________________________________
+void config_file_wildcard(options_description& desc, variables_map& vm)
+{
+ desc.add_options()
+ ("outpu*", value<string>(), "the output file1")
+ ("outp*", value<string>(), "the output file2")
+ ;
+ istringstream is("output1=whichone\noutput2=whichone\n");
+ store(parse_config_file(is, desc), vm);
+}
+
+//________________________________________________________________________________________
+//
+// invalid_syntax::unrecognized_line
+//
+//________________________________________________________________________________________
+void unrecognized_line(options_description& desc, variables_map& vm)
+{
+ istringstream is("funny wierd line\n");
+ store(parse_config_file(is, desc), vm);
+}
+
+//________________________________________________________________________________________
+//
+// abbreviated_options_in_config_file
+//
+//________________________________________________________________________________________
+void abbreviated_options_in_config_file(options_description& desc, variables_map& vm)
+{
+ desc.add_options()(",o", value<string>(), "the output file");
+ istringstream is("o=output.txt\n");
+ store(parse_config_file(is, desc), vm);
+}
+
+
+//________________________________________________________________________________________
+//
+// too_many_positional_options
+//
+//________________________________________________________________________________________
+void too_many_positional_options(options_description& desc, variables_map& vm)
+{
+ const char* argv[] = {"program", "1", "2", "3"};
+ positional_options_description positional_args;
+ positional_args.add("two_positional_arguments", 2);
+ store(command_line_parser(4, argv).options(desc).positional(positional_args).run(), vm);
+}
+
+
+//________________________________________________________________________________________
+//
+// invalid_command_line_style
+//
+//________________________________________________________________________________________
+
+void test_invalid_command_line_style_exception_msg()
+{
+ string test_name = "invalid_command_line_style";
+ using namespace command_line_style;
+ options_description desc;
+ desc.add_options()("output,o", value<string>(), "the output file");
+
+ vector<int> invalid_styles;
+ invalid_styles.push_back(allow_short | short_allow_adjacent);
+ invalid_styles.push_back(allow_short | allow_dash_for_short);
+ invalid_styles.push_back(allow_long);
+ vector<string> invalid_diagnostics;
+ invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
+ "or other of command_line_style::allow_slash_for_short "
+ "(slashes) or command_line_style::allow_dash_for_short "
+ "(dashes) for short options.");
+ invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
+ "or other of 'command_line_style::short_allow_next' "
+ "(whitespace separated arguments) or "
+ "command_line_style::short_allow_adjacent ('=' "
+ "separated arguments) for short options.");
+ invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
+ "or other of 'command_line_style::long_allow_next' "
+ "(whitespace separated arguments) or "
+ "command_line_style::long_allow_adjacent ('=' "
+ "separated arguments) for long options.");
+
+
+ const char* argv[] = {"program"};
+ variables_map vm;
+ for (unsigned ii = 0; ii < 3; ++ii)
+ {
+ bool exception_thrown = false;
+ try
+ {
+ store(parse_command_line(1, argv, desc, invalid_styles[ii]), vm);
+ }
+ catch (invalid_command_line_style& e)
+ {
+ string error_msg("arguments are not allowed for unabbreviated option names");
+ CHECK_EQUAL(test_name, e.what(), invalid_diagnostics[ii]);
+ exception_thrown = true;
+ }
+ catch (std::exception& e)
+ {
+ BOOST_ERROR(string(test_name + ":\nUnexpected exception. ") + e.what() +
+ "\nExpected text:\n" + invalid_diagnostics[ii] + "\n");
+ exception_thrown = true;
+ }
+ if (!exception_thrown)
+ {
+ BOOST_ERROR(test_name << ": No exception thrown. ");
+ }
+ }
+}
+
+
+
+int main(int /*ac*/, char** /*av*/)
+{
+ test_ambiguous_option_exception_msg();
+ test_unknown_option_exception_msg();
+ test_multiple_occurrences_exception_msg();
+ test_missing_value_exception_msg();
+ test_invalid_option_value_exception_msg();
+ test_invalid_bool_value_exception_msg();
+ test_multiple_values_not_allowed_exception_msg();
+ test_required_option_exception_msg();
+ test_at_least_one_value_required_exception_msg();
+
+ string test_name;
+ string expected_message;
+
+
+ // check_reading_file
+ test_name = "check_reading_file";
+ expected_message = "can not read options configuration file 'no_such_file'";
+ test_exception<reading_file>(test_name, expected_message, check_reading_file);
+
+ // config_file_wildcard
+ test_name = "config_file_wildcard";
+ expected_message = "options 'outpu*' and 'outp*' will both match the same arguments from the configuration file";
+ test_exception<error>(test_name, expected_message, config_file_wildcard);
+
+ // unrecognized_line
+ test_name = "unrecognized_line";
+ expected_message = "the options configuration file contains an invalid line 'funny wierd line'";
+ test_exception<invalid_syntax>(test_name, expected_message, unrecognized_line);
+
+
+ // abbreviated_options_in_config_file
+ test_name = "abbreviated_options_in_config_file";
+ expected_message = "abbreviated option names are not permitted in options configuration files";
+ test_exception<error>(test_name, expected_message, abbreviated_options_in_config_file);
+
+ test_name = "too_many_positional_options";
+ expected_message = "too many positional arguments have been specified on the command line";
+ test_exception<too_many_positional_options_error>(
+ test_name, expected_message, too_many_positional_options);
+
+ test_invalid_command_line_style_exception_msg();
+
+
+ return 0;
+}
+
+
+
+// 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/parsers.hpp>
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/cmdline.hpp>
+using namespace boost::program_options;
+
+#include <iostream>
+#include <sstream>
+#include <vector>
+#include <cassert>
+using namespace std;
+
+#include "minitest.hpp"
+
+
+
+//
+// like BOOST_CHECK_EQUAL but with more descriptive error message
+//
+#define CHECK_EQUAL(description, a, b) if (a != b) {std::cerr << "\n\nError:\n<<" << \
+ description << ">>\n Expected text=\"" << b << "\"\n Actual text =\"" << a << "\"\n\n"; assert(a == b);}
+
+
+// Uncomment for Debugging, removes asserts so we can see more failures!
+//#define BOOST_ERROR(description) std::cerr << description; std::cerr << "\n";
+
+
+//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
+//
+// Uncomment to print out the complete set of diagnostic messages for the different test cases
+/*
+#define CHECK_EQUAL(description, a, b) if (a != b) {std::cerr << "\n\nError: " << \
+ description << "\n Expecting\n" << b << "\n Found\n" << a << "\n\n"; } \
+ else {std::cout << description<< "\t" << b << "\n";}
+*/
+
+//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
+
+
+//
+// test exception for each specified command line style, e.g. short dash or config file
+//
+template<typename EXCEPTION>
+void test_each_exception_message(const string& test_description, const vector<const char*>& argv, options_description& desc, int style, string exception_msg, istream& is = cin)
+{
+ if (exception_msg.length() == 0)
+ return;
+ variables_map vm;
+ unsigned argc = argv.size();
+
+
+ try {
+ if (style == -1)
+ store(parse_config_file(is, desc), vm);
+ else
+ store(parse_command_line(argv.size(), argv.data(), desc, style), vm);
+ notify(vm);
+ }
+ catch (EXCEPTION& e)
+ {
+ //cerr << "Correct:\n\t" << e.what() << "\n";
+ CHECK_EQUAL(test_description, e.what(), exception_msg);
+ return;
+ }
+ catch (std::exception& e)
+ {
+ // concatenate argv without boost::algorithm::join
+ string argv_txt;
+ for (unsigned ii = 0; ii < argc - 1; ++ii)
+ argv_txt += argv[ii] + string(" ");
+ if (argc)
+ argv_txt += argv[argc - 1];
+
+ BOOST_ERROR("\n<<" + test_description +
+ string(">>\n Unexpected exception type!\n Actual text =\"") + e.what() +
+ "\"\n argv =\"" + argv_txt +
+ "\"\n Expected text=\"" + exception_msg + "\"\n");
+ return;
+ }
+ BOOST_ERROR(test_description + ": No exception thrown. ");
+}
+
+
+
+
+//
+// test exception messages for all command line styles (unix/long/short/slash/config file)
+//
+// try each command line style in turn
+const int unix_style = command_line_style::unix_style;
+const int short_dash = command_line_style::allow_dash_for_short | command_line_style::allow_short | command_line_style::short_allow_adjacent | command_line_style::allow_sticky;
+const int short_slash = command_line_style::allow_slash_for_short | command_line_style::allow_short | command_line_style::short_allow_adjacent;
+const int long_dash = command_line_style::allow_long | command_line_style::long_allow_adjacent | command_line_style::allow_guessing;
+
+
+
+template<typename EXCEPTION>
+void test_exception_message(const vector<vector<const char*>>& argv,
+ options_description& desc,
+ const string& error_description,
+ const char* expected_message_template[5])
+{
+ string expected_message;
+
+ // unix
+ expected_message = expected_message_template[0];
+ test_each_exception_message<EXCEPTION>(error_description + " -- unix",
+ argv[0], desc, unix_style, expected_message);
+
+ // long dash only
+ expected_message = expected_message_template[1];
+ test_each_exception_message<EXCEPTION>(error_description + " -- long_dash",
+ argv[1], desc, long_dash, expected_message);
+
+
+ // short dash only
+ expected_message = expected_message_template[2];
+ test_each_exception_message<EXCEPTION>(error_description + " -- short_dash",
+ argv[2], desc, short_dash, expected_message);
+
+ // short slash only
+ expected_message = expected_message_template[3];
+ test_each_exception_message<EXCEPTION>(error_description + " -- short_slash",
+ argv[3], desc, short_slash, expected_message);
+
+ // config file only
+ expected_message = expected_message_template[4];
+ if (expected_message.length())
+ {
+ istringstream istrm(argv[4][0]);
+ test_each_exception_message<EXCEPTION>(error_description + " -- config_file",
+ argv[4], desc, -1, expected_message, istrm);
+ }
+
+}
+
+#define VEC_STR_PUSH_BACK(vec, c_array) \
+ vec.push_back(vector<const char*>(c_array, c_array + sizeof(c_array) / sizeof(char*)));
+
+//________________________________________________________________________________________
+//
+// invalid_option_value
+//
+//________________________________________________________________________________________
+void test_invalid_option_value_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("int_option,d", value< int >(), "An option taking an integer")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--int", "A_STRING"}; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "int_option=A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv4);
+
+ const char* expected_msg[5] = {
+ "the argument ('A_STRING') for option '--int_option' is invalid",
+ "the argument ('A_STRING') for option '--int_option' is invalid",
+ "the argument ('A_STRING') for option '-d' is invalid",
+ "the argument ('A_STRING') for option '/d' is invalid",
+ "the argument ('A_STRING') for option 'int_option' is invalid"
+ };
+
+
+ test_exception_message<invalid_option_value>(argv, desc, "invalid_option_value",
+ expected_msg);
+
+
+}
+
+//________________________________________________________________________________________
+//
+// missing_value
+//
+//________________________________________________________________________________________
+void test_missing_value_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,e", value<string>(), "the config file")
+ ("output,o", value<string>(), "the output file")
+ ;
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-e", "-e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--cfgfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-e", "-e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/e", "/e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { ""} ; VEC_STR_PUSH_BACK(argv, argv4);
+
+ const char* expected_msg[5] = {
+ "the required argument for option '--cfgfile' is missing",
+ "the required argument for option '--cfgfile' is missing",
+ "the required argument for option '-e' is missing",
+ "", // Ignore probable bug in cmdline::finish_option
+ //"the required argument for option '/e' is missing",
+ "",
+ };
+ test_exception_message<invalid_command_line_syntax>(argv, desc,
+ "invalid_syntax::missing_parameter",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// ambiguous_option
+//
+//________________________________________________________________________________________
+void test_ambiguous_option_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile1,c", value<string>(), "the config file")
+ ("cfgfile2,o", value<string>(), "the config file")
+ ("good,g", "good option")
+ ("output,c", value<string>(), "the output file")
+ ("output", value<string>(), "the output file")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-ggc", "file", "-o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--cfgfile", "file", "--cfgfile", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-ggc", "file", "-o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/c", "file", "/o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "output=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '-c' is ambiguous and matches '--cfgfile1', and '--output'",
+ "option '--cfgfile' is ambiguous and matches '--cfgfile1', and '--cfgfile2'",
+ "option '-c' is ambiguous",
+ "option '/c' is ambiguous",
+ "option 'output' is ambiguous and matches different versions of 'output'",
+ };
+ test_exception_message<ambiguous_option>(argv, desc, "ambiguous_option",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// multiple_occurrences
+//
+//________________________________________________________________________________________
+void test_multiple_occurrences_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<string>(), "the configfile")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-c", "file", "-c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--cfgfi", "file", "--cfgfi", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-c", "file", "-c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/c", "file", "/c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "cfgfile=output.txt\ncfgfile=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '--cfgfile' cannot be specified more than once",
+ "option '--cfgfile' cannot be specified more than once",
+ "option '-c' cannot be specified more than once",
+ "option '/c' cannot be specified more than once",
+ "option 'cfgfile' cannot be specified more than once",
+ };
+ test_exception_message<multiple_occurrences>(argv, desc, "multiple_occurrences",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// unknown_option
+//
+//________________________________________________________________________________________
+void test_unknown_option_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("good,g", "good option")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-ggc", "file"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--cfgfile", "file"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-ggc", "file"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/c", "file"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "cfgfile=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "unrecognised option '-ggc'",
+ "unrecognised option '--cfgfile'",
+ "unrecognised option '-ggc'",
+ "unrecognised option '/c'",
+ "unrecognised option 'cfgfile'",
+ };
+ test_exception_message<unknown_option>(argv, desc, "unknown_option", expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// validation_error::invalid_bool_value
+//
+//________________________________________________________________________________________
+void test_invalid_bool_value_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("bool_option,b", value< bool>(), "bool_option")
+ ;
+
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-b", "file"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--bool_optio", "file"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-b", "file"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/b", "file"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "bool_option=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "the argument ('file') for option '--bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('file') for option '--bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('file') for option '-b' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('file') for option '/b' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('output.txt') for option 'bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ };
+ test_exception_message<validation_error>(argv,
+ desc,
+ "validation_error::invalid_bool_value",
+ expected_msg);
+}
+
+
+
+
+//________________________________________________________________________________________
+//
+// validation_error::multiple_values_not_allowed
+//
+//________________________________________________________________________________________
+//
+// Strange exception: sole purpose seems to be catching multitoken() associated with a scalar
+// validation_error::multiple_values_not_allowed seems thus to be a programmer error
+//
+//
+void test_multiple_values_not_allowed_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<string>()->multitoken(), "the config file")
+ ("good,g", "good option")
+ ("output,o", value<string>(), "the output file")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-c", "file", "c", "-o", "fritz", "hugo" } ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--cfgfil", "file", "c", "--outpu", "fritz", "hugo" } ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-c", "file", "c", "-o", "fritz", "hugo"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/c", "file", "c", "/o", "fritz", "hugo"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '--cfgfile' only takes a single argument",
+ "option '--cfgfile' only takes a single argument",
+ "option '-c' only takes a single argument",
+ "option '/c' only takes a single argument",
+ "",
+ };
+ test_exception_message<validation_error>(argv,
+ desc,
+ "validation_error::multiple_values_not_allowed",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// validation_error::at_least_one_value_required
+//
+//________________________________________________________________________________________
+//
+// Strange exception: sole purpose seems to be catching zero_tokens() associated with a scalar
+// validation_error::multiple_values_not_allowed seems thus to be a programmer error
+//
+//
+void test_at_least_one_value_required_exception_msg()
+{
+
+
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<int>()->zero_tokens(), "the config file")
+ ("other,o", value<string>(), "other")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-c" } ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--cfg", "--o", "name" } ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-c" , "-o" , "name" } ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/c" } ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '--cfgfile' requires at least one argument",
+ "option '--cfgfile' requires at least one argument",
+ "option '-c' requires at least one argument",
+ "option '/c' requires at least one argument",
+ "",
+ };
+ test_exception_message<validation_error>(argv,
+ desc,
+ "validation_error::at_least_one_value_required",
+ expected_msg);
+}
+
+
+//________________________________________________________________________________________
+//
+// required_option
+//
+//________________________________________________________________________________________
+void test_required_option_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<string>()->required(), "the config file")
+ ("good,g", "good option")
+ ("output,o", value<string>()->required(), "the output file")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-g" } ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--g" } ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-g"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/g"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "the option '--cfgfile' is required but missing",
+ "the option '--cfgfile' is required but missing",
+ "the option '-c' is required but missing",
+ "the option '/c' is required but missing",
+ "the option 'cfgfile' is required but missing",
+ };
+ test_exception_message<required_option>(argv,
+ desc,
+ "required_option",
+ expected_msg);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**
+ * Check if this is the expected exception with the right message is being thrown inside
+ * func
+*/
+template <typename EXCEPTION, typename FUNC>
+void test_exception(const string& test_name, const string& exception_txt, FUNC func)
+{
+
+ try {
+ options_description desc;
+ variables_map vm;
+ func(desc, vm);
+ }
+ catch (EXCEPTION& e)
+ {
+ CHECK_EQUAL(test_name, e.what(), exception_txt);
+ return;
+ }
+ catch (std::exception& e)
+ {
+ BOOST_ERROR(string(test_name + ":\nUnexpected exception. ") + e.what() +
+ "\nExpected text:\n" + exception_txt + "\n\n");
+ return;
+ }
+ BOOST_ERROR(test_name + ": No exception thrown. ");
+}
+
+
+
+//________________________________________________________________________________________
+//
+// check_reading_file
+//
+//________________________________________________________________________________________
+void check_reading_file(options_description& desc, variables_map& vm)
+{
+ desc.add_options()
+ ("output,o", value<string>(), "the output file");
+
+ const char* file_name = "no_such_file";
+ store(parse_config_file<char>(file_name, desc, true), vm);
+
+}
+
+
+//________________________________________________________________________________________
+//
+// config_file_wildcard
+//
+//________________________________________________________________________________________
+void config_file_wildcard(options_description& desc, variables_map& vm)
+{
+ desc.add_options()
+ ("outpu*", value<string>(), "the output file1")
+ ("outp*", value<string>(), "the output file2")
+ ;
+ istringstream is("output1=whichone\noutput2=whichone\n");
+ store(parse_config_file(is, desc), vm);
+}
+
+//________________________________________________________________________________________
+//
+// invalid_syntax::unrecognized_line
+//
+//________________________________________________________________________________________
+void unrecognized_line(options_description& desc, variables_map& vm)
+{
+ istringstream is("funny wierd line\n");
+ store(parse_config_file(is, desc), vm);
+}
+
+//________________________________________________________________________________________
+//
+// abbreviated_options_in_config_file
+//
+//________________________________________________________________________________________
+void abbreviated_options_in_config_file(options_description& desc, variables_map& vm)
+{
+ desc.add_options()(",o", value<string>(), "the output file");
+ istringstream is("o=output.txt\n");
+ store(parse_config_file(is, desc), vm);
+}
+
+
+//________________________________________________________________________________________
+//
+// too_many_positional_options
+//
+//________________________________________________________________________________________
+void too_many_positional_options(options_description& desc, variables_map& vm)
+{
+ const char* argv[] = {"program", "1", "2", "3"};
+ positional_options_description positional_args;
+ positional_args.add("two_positional_arguments", 2);
+ store(command_line_parser(4, argv).options(desc).positional(positional_args).run(), vm);
+}
+
+
+//________________________________________________________________________________________
+//
+// invalid_command_line_style
+//
+//________________________________________________________________________________________
+
+void test_invalid_command_line_style_exception_msg()
+{
+ string test_name = "invalid_command_line_style";
+ using namespace command_line_style;
+ options_description desc;
+ desc.add_options()("output,o", value<string>(), "the output file");
+
+ vector<int> invalid_styles;
+ invalid_styles.push_back(allow_short | short_allow_adjacent);
+ invalid_styles.push_back(allow_short | allow_dash_for_short);
+ invalid_styles.push_back(allow_long);
+ vector<string> invalid_diagnostics;
+ invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
+ "or other of command_line_style::allow_slash_for_short "
+ "(slashes) or command_line_style::allow_dash_for_short "
+ "(dashes) for short options.");
+ invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
+ "or other of 'command_line_style::short_allow_next' "
+ "(whitespace separated arguments) or "
+ "command_line_style::short_allow_adjacent ('=' "
+ "separated arguments) for short options.");
+ invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
+ "or other of 'command_line_style::long_allow_next' "
+ "(whitespace separated arguments) or "
+ "command_line_style::long_allow_adjacent ('=' "
+ "separated arguments) for long options.");
+
+
+ const char* argv[] = {"program"};
+ variables_map vm;
+ for (unsigned ii = 0; ii < 3; ++ii)
+ {
+ bool exception_thrown = false;
+ try
+ {
+ store(parse_command_line(1, argv, desc, invalid_styles[ii]), vm);
+ }
+ catch (invalid_command_line_style& e)
+ {
+ string error_msg("arguments are not allowed for unabbreviated option names");
+ CHECK_EQUAL(test_name, e.what(), invalid_diagnostics[ii]);
+ exception_thrown = true;
+ }
+ catch (std::exception& e)
+ {
+ BOOST_ERROR(string(test_name + ":\nUnexpected exception. ") + e.what() +
+ "\nExpected text:\n" + invalid_diagnostics[ii] + "\n");
+ exception_thrown = true;
+ }
+ if (!exception_thrown)
+ {
+ BOOST_ERROR(test_name << ": No exception thrown. ");
+ }
+ }
+}
+
+
+
+int main(int /*ac*/, char** /*av*/)
+{
+ test_ambiguous_option_exception_msg();
+ test_unknown_option_exception_msg();
+ test_multiple_occurrences_exception_msg();
+ test_missing_value_exception_msg();
+ test_invalid_option_value_exception_msg();
+ test_invalid_bool_value_exception_msg();
+ test_multiple_values_not_allowed_exception_msg();
+ test_required_option_exception_msg();
+ test_at_least_one_value_required_exception_msg();
+
+ string test_name;
+ string expected_message;
+
+
+ // check_reading_file
+ test_name = "check_reading_file";
+ expected_message = "can not read options configuration file 'no_such_file'";
+ test_exception<reading_file>(test_name, expected_message, check_reading_file);
+
+ // config_file_wildcard
+ test_name = "config_file_wildcard";
+ expected_message = "options 'outpu*' and 'outp*' will both match the same arguments from the configuration file";
+ test_exception<error>(test_name, expected_message, config_file_wildcard);
+
+ // unrecognized_line
+ test_name = "unrecognized_line";
+ expected_message = "the options configuration file contains an invalid line 'funny wierd line'";
+ test_exception<invalid_syntax>(test_name, expected_message, unrecognized_line);
+
+
+ // abbreviated_options_in_config_file
+ test_name = "abbreviated_options_in_config_file";
+ expected_message = "abbreviated option names are not permitted in options configuration files";
+ test_exception<error>(test_name, expected_message, abbreviated_options_in_config_file);
+
+ test_name = "too_many_positional_options";
+ expected_message = "too many positional arguments have been specified on the command line";
+ test_exception<too_many_positional_options_error>(
+ test_name, expected_message, too_many_positional_options);
+
+ test_invalid_command_line_style_exception_msg();
+
+
+ return 0;
+}
+
+
+
+// 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/parsers.hpp>
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/cmdline.hpp>
+using namespace boost::program_options;
+
+#include <iostream>
+#include <sstream>
+#include <vector>
+#include <cassert>
+using namespace std;
+
+#include "minitest.hpp"
+
+
+
+//
+// like BOOST_CHECK_EQUAL but with more descriptive error message
+//
+#define CHECK_EQUAL(description, a, b) if (a != b) {std::cerr << "\n\nError:\n<<" << \
+ description << ">>\n Expected text=\"" << b << "\"\n Actual text =\"" << a << "\"\n\n"; assert(a == b);}
+
+
+// Uncomment for Debugging, removes asserts so we can see more failures!
+//#define BOOST_ERROR(description) std::cerr << description; std::cerr << "\n";
+
+
+//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
+//
+// Uncomment to print out the complete set of diagnostic messages for the different test cases
+//
+//#define CHECK_EQUAL(description, a, b) if (a != b) {std::cerr << "\n\nError: " << \
+// description << "\n Expecting\n" << b << "\n Found\n" << a << "\n\n"; } \
+// else {std::cout << description<< "\t" << b << "\n";}
+
+//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
+
+
+//
+// test exception for each specified command line style, e.g. short dash or config file
+//
+template<typename EXCEPTION>
+void test_each_exception_message(const string& test_description, const vector<const char*>& argv, options_description& desc, int style, string exception_msg, istream& is = cin)
+{
+ if (exception_msg.length() == 0)
+ return;
+ variables_map vm;
+ unsigned argc = argv.size();
+
+
+ try {
+ if (style == -1)
+ store(parse_config_file(is, desc), vm);
+ else
+ store(parse_command_line(argv.size(), argv.data(), desc, style), vm);
+ notify(vm);
+ }
+ catch (EXCEPTION& e)
+ {
+ //cerr << "Correct:\n\t" << e.what() << "\n";
+ CHECK_EQUAL(test_description, e.what(), exception_msg);
+ return;
+ }
+ catch (std::exception& e)
+ {
+ // concatenate argv without boost::algorithm::join
+ string argv_txt;
+ for (unsigned ii = 0; ii < argc - 1; ++ii)
+ argv_txt += argv[ii] + string(" ");
+ if (argc)
+ argv_txt += argv[argc - 1];
+
+ BOOST_ERROR("\n<<" + test_description +
+ string(">>\n Unexpected exception type!\n Actual text =\"") + e.what() +
+ "\"\n argv =\"" + argv_txt +
+ "\"\n Expected text=\"" + exception_msg + "\"\n");
+ return;
+ }
+ BOOST_ERROR(test_description + ": No exception thrown. ");
+}
+
+
+
+
+//
+// test exception messages for all command line styles (unix/long/short/slash/config file)
+//
+// try each command line style in turn
+const int unix_style = command_line_style::unix_style;
+const int short_dash = command_line_style::allow_dash_for_short | command_line_style::allow_short | command_line_style::short_allow_adjacent | command_line_style::allow_sticky;
+const int short_slash = command_line_style::allow_slash_for_short | command_line_style::allow_short | command_line_style::short_allow_adjacent;
+const int long_dash = command_line_style::allow_long | command_line_style::long_allow_adjacent | command_line_style::allow_guessing;
+
+
+
+template<typename EXCEPTION>
+void test_exception_message(const vector<vector<const char*>>& argv,
+ options_description& desc,
+ const string& error_description,
+ const char* expected_message_template[5])
+{
+ string expected_message;
+
+ // unix
+ expected_message = expected_message_template[0];
+ test_each_exception_message<EXCEPTION>(error_description + " -- unix",
+ argv[0], desc, unix_style, expected_message);
+
+ // long dash only
+ expected_message = expected_message_template[1];
+ test_each_exception_message<EXCEPTION>(error_description + " -- long_dash",
+ argv[1], desc, long_dash, expected_message);
+
+
+ // short dash only
+ expected_message = expected_message_template[2];
+ test_each_exception_message<EXCEPTION>(error_description + " -- short_dash",
+ argv[2], desc, short_dash, expected_message);
+
+ // short slash only
+ expected_message = expected_message_template[3];
+ test_each_exception_message<EXCEPTION>(error_description + " -- short_slash",
+ argv[3], desc, short_slash, expected_message);
+
+ // config file only
+ expected_message = expected_message_template[4];
+ if (expected_message.length())
+ {
+ istringstream istrm(argv[4][0]);
+ test_each_exception_message<EXCEPTION>(error_description + " -- config_file",
+ argv[4], desc, -1, expected_message, istrm);
+ }
+
+}
+
+#define VEC_STR_PUSH_BACK(vec, c_array) \
+ vec.push_back(vector<const char*>(c_array, c_array + sizeof(c_array) / sizeof(char*)));
+
+//________________________________________________________________________________________
+//
+// invalid_option_value
+//
+//________________________________________________________________________________________
+void test_invalid_option_value_exception_msg()
+{
+ options_description desc;
+ int required_value;
+ desc.add_options()
+ ("int_option,d", value< int >(), "An option taking an integer")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--int", "A_STRING"}; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "int_option=A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv4);
+
+ const char* expected_msg[5] = {
+ "the argument ('A_STRING') for option '--int_option' is invalid",
+ "the argument ('A_STRING') for option '--int_option' is invalid",
+ "the argument ('A_STRING') for option '-d' is invalid",
+ "the argument ('A_STRING') for option '/d' is invalid",
+ "the argument ('A_STRING') for option 'int_option' is invalid"
+ };
+
+
+ test_exception_message<invalid_option_value>(argv, desc, "invalid_option_value",
+ expected_msg);
+
+
+}
+
+//________________________________________________________________________________________
+//
+// missing_value
+//
+//________________________________________________________________________________________
+void test_missing_value_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,e", value<string>(), "the config file")
+ ("output,o", value<string>(), "the output file")
+ ;
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-e", "-e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--cfgfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-e", "-e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/e", "/e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { ""} ; VEC_STR_PUSH_BACK(argv, argv4);
+
+ const char* expected_msg[5] = {
+ "the required argument for option '--cfgfile' is missing",
+ "the required argument for option '--cfgfile' is missing",
+ "the required argument for option '-e' is missing",
+ "", // Ignore probable bug in cmdline::finish_option
+ //"the required argument for option '/e' is missing",
+ "",
+ };
+ test_exception_message<invalid_command_line_syntax>(argv, desc,
+ "invalid_syntax::missing_parameter",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// ambiguous_option
+//
+//________________________________________________________________________________________
+void test_ambiguous_option_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile1,c", value<string>()->multitoken(), "the config file")
+ ("cfgfile2,o", value<string>(), "the config file")
+ ("good,g", "good option")
+ ("output,c", value<string>(), "the output file")
+ ("output", value<string>(), "the output file")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-ggc", "file", "-o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--cfgfile", "file", "--cfgfile", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-ggc", "file", "-o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/c", "file", "/o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "output=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '-c' is ambiguous and matches '--cfgfile1', and '--output'",
+ "option '--cfgfile' is ambiguous and matches '--cfgfile1', and '--cfgfile2'",
+ "option '-c' is ambiguous",
+ "option '/c' is ambiguous",
+ "option 'output' is ambiguous and matches different versions of 'output'",
+ };
+ test_exception_message<ambiguous_option>(argv, desc, "ambiguous_option",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// multiple_occurrences
+//
+//________________________________________________________________________________________
+void test_multiple_occurrences_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<string>(), "the configfile")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-c", "file", "-c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--cfgfi", "file", "--cfgfi", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-c", "file", "-c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/c", "file", "/c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "cfgfile=output.txt\ncfgfile=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '--cfgfile' cannot be specified more than once",
+ "option '--cfgfile' cannot be specified more than once",
+ "option '-c' cannot be specified more than once",
+ "option '/c' cannot be specified more than once",
+ "option 'cfgfile' cannot be specified more than once",
+ };
+ test_exception_message<multiple_occurrences>(argv, desc, "multiple_occurrences",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// unknown_option
+//
+//________________________________________________________________________________________
+void test_unknown_option_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("good,g", "good option")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-ggc", "file"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--cfgfile", "file"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-ggc", "file"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/c", "file"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "cfgfile=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "unrecognised option '-ggc'",
+ "unrecognised option '--cfgfile'",
+ "unrecognised option '-ggc'",
+ "unrecognised option '/c'",
+ "unrecognised option 'cfgfile'",
+ };
+ test_exception_message<unknown_option>(argv, desc, "unknown_option", expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// validation_error::invalid_bool_value
+//
+//________________________________________________________________________________________
+void test_invalid_bool_value_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("bool_option,b", value< bool>(), "bool_option")
+ ;
+
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-b", "file"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--bool_optio", "file"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-b", "file"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/b", "file"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "bool_option=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "the argument ('file') for option '--bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('file') for option '--bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('file') for option '-b' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('file') for option '/b' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('output.txt') for option 'bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ };
+ test_exception_message<validation_error>(argv,
+ desc,
+ "validation_error::invalid_bool_value",
+ expected_msg);
+}
+
+
+
+
+//________________________________________________________________________________________
+//
+// validation_error::multiple_values_not_allowed
+//
+//________________________________________________________________________________________
+//
+// Strange exception: sole purpose seems to be catching multitoken() not associated with a vector<string>??
+// This is surely a corner case.
+//
+void test_multiple_values_not_allowed_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<string>()->multitoken(), "the config file")
+ ("good,g", "good option")
+ ("output,o", value<string>()->multitoken(), "the output file")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-c", "file", "c", "-o", "fritz", "hugo" } ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--cfgfil", "file", "c", "--outpu", "fritz", "hugo" } ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-c", "file", "c", "-o", "fritz", "hugo"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/c", "file", "c", "/o", "fritz", "hugo"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '--cfgfile' only takes a single argument",
+ "option '--cfgfile' only takes a single argument",
+ "option '-c' only takes a single argument",
+ "option '/c' only takes a single argument",
+ "",
+ };
+ test_exception_message<validation_error>(argv,
+ desc,
+ "validation_error::multiple_values_not_allowed",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// validation_error::at_least_one_value_required
+//
+//________________________________________________________________________________________
+//
+// Can't build a test case!!!
+//
+//
+void test_at_least_one_value_required_exception_msg()
+{
+
+ return;
+
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<vector<int>>()->multitoken(), "the config file")
+ ("other,o", value<string>(), "other")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-c" } ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--cfg" } ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-c" , "-o" , "name" } ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/c" } ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "Unexpected exception. the required argument for option '--cfgfile' is missing",
+ "Unexpected exception. the required argument for option '--cfgfile' is missing",
+ "Unexpected exception. the required argument for option '-c' is missing",
+ "Unexpected exception. the required argument for option '/c' is missing",
+ "",
+ };
+ test_exception_message<validation_error>(argv,
+ desc,
+ "validation_error::at_least_one_value_required",
+ expected_msg);
+}
+
+
+//________________________________________________________________________________________
+//
+// required_option
+//
+//________________________________________________________________________________________
+void test_required_option_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<string>()->required(), "the config file")
+ ("good,g", "good option")
+ ("output,o", value<string>()->required(), "the output file")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-g" } ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--g" } ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-g"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/g"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "the option '--cfgfile' is required but missing",
+ "the option '--cfgfile' is required but missing",
+ "the option '-c' is required but missing",
+ "the option '/c' is required but missing",
+ "the option 'cfgfile' is required but missing",
+ };
+ test_exception_message<required_option>(argv,
+ desc,
+ "required_option",
+ expected_msg);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**
+ * Check if this is the expected exception with the right message is being thrown inside
+ * func
+*/
+template <typename EXCEPTION, typename FUNC>
+void test_exception(const string& test_name, const string& exception_txt, FUNC func)
+{
+
+ try {
+ options_description desc;
+ variables_map vm;
+ func(desc, vm);
+ }
+ catch (EXCEPTION& e)
+ {
+ CHECK_EQUAL(test_name, e.what(), exception_txt);
+ return;
+ }
+ catch (std::exception& e)
+ {
+ BOOST_ERROR(string(test_name + ":\nUnexpected exception. ") + e.what() +
+ "\nExpected text:\n" + exception_txt + "\n\n");
+ return;
+ }
+ BOOST_ERROR(test_name + ": No exception thrown. ");
+}
+
+
+
+//________________________________________________________________________________________
+//
+// check_reading_file
+//
+//________________________________________________________________________________________
+void check_reading_file(options_description& desc, variables_map& vm)
+{
+ desc.add_options()
+ ("output,o", value<string>(), "the output file");
+
+ const char* file_name = "no_such_file";
+ store(parse_config_file<char>(file_name, desc, true), vm);
+
+}
+
+
+//________________________________________________________________________________________
+//
+// config_file_wildcard
+//
+//________________________________________________________________________________________
+void config_file_wildcard(options_description& desc, variables_map& vm)
+{
+ desc.add_options()
+ ("outpu*", value<string>(), "the output file1")
+ ("outp*", value<string>(), "the output file2")
+ ;
+ istringstream is("output1=whichone\noutput2=whichone\n");
+ store(parse_config_file(is, desc), vm);
+}
+
+//________________________________________________________________________________________
+//
+// invalid_syntax::unrecognized_line
+//
+//________________________________________________________________________________________
+void unrecognized_line(options_description& desc, variables_map& vm)
+{
+ istringstream is("funny wierd line\n");
+ store(parse_config_file(is, desc), vm);
+}
+
+//________________________________________________________________________________________
+//
+// abbreviated_options_in_config_file
+//
+//________________________________________________________________________________________
+void abbreviated_options_in_config_file(options_description& desc, variables_map& vm)
+{
+ desc.add_options()(",o", value<string>(), "the output file");
+ istringstream is("o=output.txt\n");
+ store(parse_config_file(is, desc), vm);
+}
+
+
+//________________________________________________________________________________________
+//
+// too_many_positional_options
+//
+//________________________________________________________________________________________
+void too_many_positional_options(options_description& desc, variables_map& vm)
+{
+ const char* argv[] = {"program", "1", "2", "3"};
+ positional_options_description positional_args;
+ positional_args.add("two_positional_arguments", 2);
+ store(command_line_parser(4, argv).options(desc).positional(positional_args).run(), vm);
+}
+
+
+//________________________________________________________________________________________
+//
+// invalid_command_line_style
+//
+//________________________________________________________________________________________
+
+void test_invalid_command_line_style_exception_msg()
+{
+ string test_name = "invalid_command_line_style";
+ using namespace command_line_style;
+ unsigned basic_unix_style = (allow_short
+ | allow_long
+ | allow_sticky | allow_guessing
+ | allow_dash_for_short);
+ options_description desc;
+ desc.add_options()("output,o", value<string>(), "the output file");
+
+ vector<int> invalid_styles;
+ invalid_styles.push_back(allow_short | short_allow_adjacent);
+ invalid_styles.push_back(allow_short | allow_dash_for_short);
+ invalid_styles.push_back(allow_long);
+ vector<string> invalid_diagnostics;
+ invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
+ "or other of command_line_style::allow_slash_for_short "
+ "(slashes) or command_line_style::allow_dash_for_short "
+ "(dashes) for short options.");
+ invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
+ "or other of 'command_line_style::short_allow_next' "
+ "(whitespace separated arguments) or "
+ "command_line_style::short_allow_adjacent ('=' "
+ "separated arguments) for short options.");
+ invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
+ "or other of 'command_line_style::long_allow_next' "
+ "(whitespace separated arguments) or "
+ "command_line_style::long_allow_adjacent ('=' "
+ "separated arguments) for long options.");
+
+
+ const char* argv[] = {"program"};
+ variables_map vm;
+ for (unsigned ii = 0; ii < 3; ++ii)
+ {
+ bool exception_thrown = false;
+ try
+ {
+ store(parse_command_line(1, argv, desc, invalid_styles[ii]), vm);
+ }
+ catch (invalid_command_line_style& e)
+ {
+ string error_msg("arguments are not allowed for unabbreviated option names");
+ CHECK_EQUAL(test_name, e.what(), invalid_diagnostics[ii]);
+ exception_thrown = true;
+ }
+ catch (std::exception& e)
+ {
+ BOOST_ERROR(string(test_name + ":\nUnexpected exception. ") + e.what() +
+ "\nExpected text:\n" + invalid_diagnostics[ii] + "\n");
+ exception_thrown = true;
+ }
+ if (!exception_thrown)
+ {
+ BOOST_ERROR(test_name << ": No exception thrown. ");
+ }
+ }
+}
+
+
+int main(int /*ac*/, char** /*av*/)
+{
+ test_ambiguous_option_exception_msg();
+ test_unknown_option_exception_msg();
+ test_multiple_occurrences_exception_msg();
+ test_missing_value_exception_msg();
+ test_invalid_option_value_exception_msg();
+ test_invalid_bool_value_exception_msg();
+ test_multiple_values_not_allowed_exception_msg();
+ test_required_option_exception_msg();
+ test_at_least_one_value_required_exception_msg();
+
+
+ string test_name;
+ string expected_message;
+
+
+ // check_reading_file
+ test_name = "check_reading_file";
+ expected_message = "can not read options configuration file 'no_such_file'";
+ test_exception<reading_file>(test_name, expected_message, check_reading_file);
+
+ // config_file_wildcard
+ test_name = "config_file_wildcard";
+ expected_message = "options 'outpu*' and 'outp*' will both match the same arguments from the configuration file";
+ test_exception<error>(test_name, expected_message, config_file_wildcard);
+
+ // unrecognized_line
+ test_name = "unrecognized_line";
+ expected_message = "the options configuration file contains an invalid line 'funny wierd line'";
+ test_exception<invalid_syntax>(test_name, expected_message, unrecognized_line);
+
+
+ // abbreviated_options_in_config_file
+ test_name = "abbreviated_options_in_config_file";
+ expected_message = "abbreviated option names are not permitted in options configuration files";
+ test_exception<error>(test_name, expected_message, abbreviated_options_in_config_file);
+
+ test_name = "too_many_positional_options";
+ expected_message = "too many positional arguments have been specified on the command line";
+ test_exception<too_many_positional_options_error>(
+ test_name, expected_message, too_many_positional_options);
+
+ test_invalid_command_line_style_exception_msg();
+ return 0;
+}
+
+
+
+// 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/parsers.hpp>
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/cmdline.hpp>
+using namespace boost::program_options;
+
+#include <iostream>
+#include <sstream>
+#include <vector>
+#include <cassert>
+using namespace std;
+
+#include "minitest.hpp"
+
+
+
+//
+// like BOOST_CHECK_EQUAL but with more descriptive error message
+//
+#define CHECK_EQUAL(description, a, b) if (a != b) {std::cerr << "\n\nError:\n<<" << \
+ description << ">>\n Expected text=\"" << b << "\"\n Actual text =\"" << a << "\"\n\n"; assert(a == b);}
+
+
+// Uncomment for Debugging, removes asserts so we can see more failures!
+//#define BOOST_ERROR(description) std::cerr << description; std::cerr << "\n";
+
+
+//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
+//
+// Uncomment to print out the complete set of diagnostic messages for the different test cases
+//
+//#define CHECK_EQUAL(description, a, b) if (a != b) {std::cerr << "\n\nError: " << \
+// description << "\n Expecting\n" << b << "\n Found\n" << a << "\n\n"; } \
+// else {std::cout << description<< "\t" << b << "\n";}
+
+//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
+
+
+//
+// test exception for each specified command line style, e.g. short dash or config file
+//
+template<typename EXCEPTION>
+void test_each_exception_message(const string& test_description, const vector<const char*>& argv, options_description& desc, int style, string exception_msg, istream& is = cin)
+{
+ if (exception_msg.length() == 0)
+ return;
+ variables_map vm;
+ unsigned argc = argv.size();
+
+
+ try {
+ if (style == -1)
+ store(parse_config_file(is, desc), vm);
+ else
+ store(parse_command_line(argv.size(), argv.data(), desc, style), vm);
+ notify(vm);
+ }
+ catch (EXCEPTION& e)
+ {
+ //cerr << "Correct:\n\t" << e.what() << "\n";
+ CHECK_EQUAL(test_description, e.what(), exception_msg);
+ return;
+ }
+ catch (std::exception& e)
+ {
+ // concatenate argv without boost::algorithm::join
+ string argv_txt;
+ for (unsigned ii = 0; ii < argc - 1; ++ii)
+ argv_txt += argv[ii] + string(" ");
+ if (argc)
+ argv_txt += argv[argc - 1];
+
+ BOOST_ERROR("\n<<" + test_description +
+ string(">>\n Unexpected exception type!\n Actual text =\"") + e.what() +
+ "\"\n argv =\"" + argv_txt +
+ "\"\n Expected text=\"" + exception_msg + "\"\n");
+ return;
+ }
+ BOOST_ERROR(test_description + ": No exception thrown. ");
+}
+
+
+
+
+//
+// test exception messages for all command line styles (unix/long/short/slash/config file)
+//
+// try each command line style in turn
+const int unix_style = command_line_style::unix_style;
+const int short_dash = command_line_style::allow_dash_for_short | command_line_style::allow_short | command_line_style::short_allow_adjacent | command_line_style::allow_sticky;
+const int short_slash = command_line_style::allow_slash_for_short | command_line_style::allow_short | command_line_style::short_allow_adjacent;
+const int long_dash = command_line_style::allow_long | command_line_style::long_allow_adjacent | command_line_style::allow_guessing;
+
+
+
+template<typename EXCEPTION>
+void test_exception_message(const vector<vector<const char*>>& argv,
+ options_description& desc,
+ const string& error_description,
+ const char* expected_message_template[5])
+{
+ string expected_message;
+
+ // unix
+ expected_message = expected_message_template[0];
+ test_each_exception_message<EXCEPTION>(error_description + " -- unix",
+ argv[0], desc, unix_style, expected_message);
+
+ // long dash only
+ expected_message = expected_message_template[1];
+ test_each_exception_message<EXCEPTION>(error_description + " -- long_dash",
+ argv[1], desc, long_dash, expected_message);
+
+
+ // short dash only
+ expected_message = expected_message_template[2];
+ test_each_exception_message<EXCEPTION>(error_description + " -- short_dash",
+ argv[2], desc, short_dash, expected_message);
+
+ // short slash only
+ expected_message = expected_message_template[3];
+ test_each_exception_message<EXCEPTION>(error_description + " -- short_slash",
+ argv[3], desc, short_slash, expected_message);
+
+ // config file only
+ expected_message = expected_message_template[4];
+ if (expected_message.length())
+ {
+ istringstream istrm(argv[4][0]);
+ test_each_exception_message<EXCEPTION>(error_description + " -- config_file",
+ argv[4], desc, -1, expected_message, istrm);
+ }
+
+}
+
+#define VEC_STR_PUSH_BACK(vec, c_array) \
+ vec.push_back(vector<const char*>(c_array, c_array + sizeof(c_array) / sizeof(char*)));
+
+//________________________________________________________________________________________
+//
+// invalid_option_value
+//
+//________________________________________________________________________________________
+void test_invalid_option_value_exception_msg()
+{
+ options_description desc;
+ int required_value;
+ desc.add_options()
+ ("int_option,d", value< int >(), "An option taking an integer")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--int", "A_STRING"}; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "int_option=A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv4);
+
+ const char* expected_msg[5] = {
+ "the argument ('A_STRING') for option '--int_option' is invalid",
+ "the argument ('A_STRING') for option '--int_option' is invalid",
+ "the argument ('A_STRING') for option '-d' is invalid",
+ "the argument ('A_STRING') for option '/d' is invalid",
+ "the argument ('A_STRING') for option 'int_option' is invalid"
+ };
+
+
+ test_exception_message<invalid_option_value>(argv, desc, "invalid_option_value",
+ expected_msg);
+
+
+}
+
+//________________________________________________________________________________________
+//
+// missing_value
+//
+//________________________________________________________________________________________
+void test_missing_value_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,e", value<string>(), "the config file")
+ ("output,o", value<string>(), "the output file")
+ ;
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-e", "-e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--cfgfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-e", "-e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/e", "/e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { ""} ; VEC_STR_PUSH_BACK(argv, argv4);
+
+ const char* expected_msg[5] = {
+ "the required argument for option '--cfgfile' is missing",
+ "the required argument for option '--cfgfile' is missing",
+ "the required argument for option '-e' is missing",
+ "", // Ignore probable bug in cmdline::finish_option
+ //"the required argument for option '/e' is missing",
+ "",
+ };
+ test_exception_message<invalid_command_line_syntax>(argv, desc,
+ "invalid_syntax::missing_parameter",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// ambiguous_option
+//
+//________________________________________________________________________________________
+void test_ambiguous_option_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile1,c", value<string>()->multitoken(), "the config file")
+ ("cfgfile2,o", value<string>(), "the config file")
+ ("good,g", "good option")
+ ("output,c", value<string>(), "the output file")
+ ("output", value<string>(), "the output file")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-ggc", "file", "-o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--cfgfile", "file", "--cfgfile", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-ggc", "file", "-o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/c", "file", "/o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "output=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '-c' is ambiguous and matches '--cfgfile1', and '--output'",
+ "option '--cfgfile' is ambiguous and matches '--cfgfile1', and '--cfgfile2'",
+ "option '-c' is ambiguous",
+ "option '/c' is ambiguous",
+ "option 'output' is ambiguous and matches different versions of 'output'",
+ };
+ test_exception_message<ambiguous_option>(argv, desc, "ambiguous_option",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// multiple_occurrences
+//
+//________________________________________________________________________________________
+void test_multiple_occurrences_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<string>(), "the configfile")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-c", "file", "-c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--cfgfi", "file", "--cfgfi", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-c", "file", "-c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/c", "file", "/c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "cfgfile=output.txt\ncfgfile=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '--cfgfile' cannot be specified more than once",
+ "option '--cfgfile' cannot be specified more than once",
+ "option '-c' cannot be specified more than once",
+ "option '/c' cannot be specified more than once",
+ "option 'cfgfile' cannot be specified more than once",
+ };
+ test_exception_message<multiple_occurrences>(argv, desc, "multiple_occurrences",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// unknown_option
+//
+//________________________________________________________________________________________
+void test_unknown_option_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("good,g", "good option")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-ggc", "file"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--cfgfile", "file"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-ggc", "file"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/c", "file"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "cfgfile=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "unrecognised option '-ggc'",
+ "unrecognised option '--cfgfile'",
+ "unrecognised option '-ggc'",
+ "unrecognised option '/c'",
+ "unrecognised option 'cfgfile'",
+ };
+ test_exception_message<unknown_option>(argv, desc, "unknown_option", expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// validation_error::invalid_bool_value
+//
+//________________________________________________________________________________________
+void test_invalid_bool_value_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("bool_option,b", value< bool>(), "bool_option")
+ ;
+
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-b", "file"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--bool_optio", "file"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-b", "file"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/b", "file"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "bool_option=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "the argument ('file') for option '--bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('file') for option '--bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('file') for option '-b' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('file') for option '/b' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('output.txt') for option 'bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ };
+ test_exception_message<validation_error>(argv,
+ desc,
+ "validation_error::invalid_bool_value",
+ expected_msg);
+}
+
+
+
+
+//________________________________________________________________________________________
+//
+// validation_error::multiple_values_not_allowed
+//
+//________________________________________________________________________________________
+//
+// Strange exception: sole purpose seems to be catching multitoken() not associated with a vector<string>??
+// This is surely a corner case.
+//
+void test_multiple_values_not_allowed_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<string>()->multitoken(), "the config file")
+ ("good,g", "good option")
+ ("output,o", value<string>()->multitoken(), "the output file")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-c", "file", "c", "-o", "fritz", "hugo" } ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--cfgfil", "file", "c", "--outpu", "fritz", "hugo" } ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-c", "file", "c", "-o", "fritz", "hugo"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/c", "file", "c", "/o", "fritz", "hugo"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '--cfgfile' only takes a single argument",
+ "option '--cfgfile' only takes a single argument",
+ "option '-c' only takes a single argument",
+ "option '/c' only takes a single argument",
+ "",
+ };
+ test_exception_message<validation_error>(argv,
+ desc,
+ "validation_error::multiple_values_not_allowed",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// validation_error::at_least_one_value_required
+//
+//________________________________________________________________________________________
+//
+// Can't build a test case!!!
+//
+//
+void test_at_least_one_value_required_exception_msg()
+{
+
+ return;
+
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<vector<int>>()->multitoken(), "the config file")
+ ("other,o", value<string>(), "other")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-c" } ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--cfg" } ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-c" , "-o" , "name" } ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/c" } ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "Unexpected exception. the required argument for option '--cfgfile' is missing",
+ "Unexpected exception. the required argument for option '--cfgfile' is missing",
+ "Unexpected exception. the required argument for option '-c' is missing",
+ "Unexpected exception. the required argument for option '/c' is missing",
+ "",
+ };
+ test_exception_message<validation_error>(argv,
+ desc,
+ "validation_error::at_least_one_value_required",
+ expected_msg);
+}
+
+
+//________________________________________________________________________________________
+//
+// required_option
+//
+//________________________________________________________________________________________
+void test_required_option_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<string>()->required(), "the config file")
+ ("good,g", "good option")
+ ("output,o", value<string>()->required(), "the output file")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-g" } ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--g" } ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-g"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/g"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "the option '--cfgfile' is required but missing",
+ "the option '--cfgfile' is required but missing",
+ "the option '-c' is required but missing",
+ "the option '/c' is required but missing",
+ "the option 'cfgfile' is required but missing",
+ };
+ test_exception_message<required_option>(argv,
+ desc,
+ "required_option",
+ expected_msg);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**
+ * Check if this is the expected exception with the right message is being thrown inside
+ * func
+*/
+template <typename EXCEPTION, typename FUNC>
+void test_exception(const string& test_name, const string& exception_txt, FUNC func)
+{
+
+ try {
+ options_description desc;
+ variables_map vm;
+ func(desc, vm);
+ }
+ catch (EXCEPTION& e)
+ {
+ CHECK_EQUAL(test_name, e.what(), exception_txt);
+ return;
+ }
+ catch (std::exception& e)
+ {
+ BOOST_ERROR(string(test_name + ":\nUnexpected exception. ") + e.what() +
+ "\nExpected text:\n" + exception_txt + "\n\n");
+ return;
+ }
+ BOOST_ERROR(test_name + ": No exception thrown. ");
+}
+
+
+
+//________________________________________________________________________________________
+//
+// check_reading_file
+//
+//________________________________________________________________________________________
+void check_reading_file(options_description& desc, variables_map& vm)
+{
+ desc.add_options()
+ ("output,o", value<string>(), "the output file");
+
+ const char* file_name = "no_such_file";
+ store(parse_config_file<char>(file_name, desc, true), vm);
+
+}
+
+
+//________________________________________________________________________________________
+//
+// config_file_wildcard
+//
+//________________________________________________________________________________________
+void config_file_wildcard(options_description& desc, variables_map& vm)
+{
+ desc.add_options()
+ ("outpu*", value<string>(), "the output file1")
+ ("outp*", value<string>(), "the output file2")
+ ;
+ istringstream is("output1=whichone\noutput2=whichone\n");
+ store(parse_config_file(is, desc), vm);
+}
+
+//________________________________________________________________________________________
+//
+// invalid_syntax::unrecognized_line
+//
+//________________________________________________________________________________________
+void unrecognized_line(options_description& desc, variables_map& vm)
+{
+ istringstream is("funny wierd line\n");
+ store(parse_config_file(is, desc), vm);
+}
+
+//________________________________________________________________________________________
+//
+// abbreviated_options_in_config_file
+//
+//________________________________________________________________________________________
+void abbreviated_options_in_config_file(options_description& desc, variables_map& vm)
+{
+ desc.add_options()(",o", value<string>(), "the output file");
+ istringstream is("o=output.txt\n");
+ store(parse_config_file(is, desc), vm);
+}
+
+
+//________________________________________________________________________________________
+//
+// too_many_positional_options
+//
+//________________________________________________________________________________________
+void too_many_positional_options(options_description& desc, variables_map& vm)
+{
+ const char* argv[] = {"program", "1", "2", "3"};
+ positional_options_description positional_args;
+ positional_args.add("two_positional_arguments", 2);
+ store(command_line_parser(4, argv).options(desc).positional(positional_args).run(), vm);
+}
+
+
+//________________________________________________________________________________________
+//
+// invalid_command_line_style
+//
+//________________________________________________________________________________________
+
+void test_invalid_command_line_style_exception_msg()
+{
+ string test_name = "invalid_command_line_style";
+ using namespace command_line_style;
+ unsigned basic_unix_style = (allow_short
+ | allow_long
+ | allow_sticky | allow_guessing
+ | allow_dash_for_short);
+ options_description desc;
+ desc.add_options()("output,o", value<string>(), "the output file");
+
+ vector<int> invalid_styles;
+ invalid_styles.push_back(allow_short | short_allow_adjacent);
+ invalid_styles.push_back(allow_short | allow_dash_for_short);
+ invalid_styles.push_back(allow_long);
+ vector<string> invalid_diagnostics;
+ invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
+ "or other of command_line_style::allow_slash_for_short "
+ "(slashes) or command_line_style::allow_dash_for_short "
+ "(dashes) for short options.");
+ invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
+ "or other of 'command_line_style::short_allow_next' "
+ "(whitespace separated arguments) or "
+ "command_line_style::short_allow_adjacent ('=' "
+ "separated arguments) for short options.");
+ invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
+ "or other of 'command_line_style::long_allow_next' "
+ "(whitespace separated arguments) or "
+ "command_line_style::long_allow_adjacent ('=' "
+ "separated arguments) for long options.");
+
+
+ const char* argv[] = {"program"};
+ variables_map vm;
+ for (unsigned ii = 0; ii < 3; ++ii)
+ {
+ bool exception_thrown = false;
+ try
+ {
+ store(parse_command_line(1, argv, desc, invalid_styles[ii]), vm);
+ }
+ catch (invalid_command_line_style& e)
+ {
+ string error_msg("arguments are not allowed for unabbreviated option names");
+ CHECK_EQUAL(test_name, e.what(), invalid_diagnostics[ii]);
+ exception_thrown = true;
+ }
+ catch (std::exception& e)
+ {
+ BOOST_ERROR(string(test_name + ":\nUnexpected exception. ") + e.what() +
+ "\nExpected text:\n" + invalid_diagnostics[ii] + "\n");
+ exception_thrown = true;
+ }
+ if (!exception_thrown)
+ {
+ BOOST_ERROR(test_name << ": No exception thrown. ");
+ }
+ }
+}
+
+
+int main(int /*ac*/, char** /*av*/)
+{
+ test_ambiguous_option_exception_msg();
+ test_unknown_option_exception_msg();
+ test_multiple_occurrences_exception_msg();
+ test_missing_value_exception_msg();
+ test_invalid_option_value_exception_msg();
+ test_invalid_bool_value_exception_msg();
+ test_multiple_values_not_allowed_exception_msg();
+ test_required_option_exception_msg();
+ test_at_least_one_value_required_exception_msg();
+
+
+ string test_name;
+ string expected_message;
+
+
+ // check_reading_file
+ test_name = "check_reading_file";
+ expected_message = "can not read options configuration file 'no_such_file'";
+ test_exception<reading_file>(test_name, expected_message, check_reading_file);
+
+ // config_file_wildcard
+ test_name = "config_file_wildcard";
+ expected_message = "options 'outpu*' and 'outp*' will both match the same arguments from the configuration file";
+ test_exception<error>(test_name, expected_message, config_file_wildcard);
+
+ // unrecognized_line
+ test_name = "unrecognized_line";
+ expected_message = "the options configuration file contains an invalid line 'funny wierd line'";
+ test_exception<invalid_syntax>(test_name, expected_message, unrecognized_line);
+
+
+ // abbreviated_options_in_config_file
+ test_name = "abbreviated_options_in_config_file";
+ expected_message = "abbreviated option names are not permitted in options configuration files";
+ test_exception<error>(test_name, expected_message, abbreviated_options_in_config_file);
+
+ test_name = "too_many_positional_options";
+ expected_message = "too many positional arguments have been specified on the command line";
+ test_exception<too_many_positional_options_error>(
+ test_name, expected_message, too_many_positional_options);
+
+ test_invalid_command_line_style_exception_msg();
+ return 0;
+}
+
+
+
+// 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/parsers.hpp>
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/cmdline.hpp>
+using namespace boost::program_options;
+
+#include <iostream>
+#include <sstream>
+#include <vector>
+#include <cassert>
+using namespace std;
+
+#include "minitest.hpp"
+
+
+
+//
+// like BOOST_CHECK_EQUAL but with more descriptive error message
+//
+#define CHECK_EQUAL(description, a, b) if (a != b) {std::cerr << "\n\nError:\n<<" << \
+ description << ">>\n Expected text=\"" << b << "\"\n Actual text =\"" << a << "\"\n\n"; assert(a == b);}
+
+
+// Uncomment for Debugging, removes asserts so we can see more failures!
+//#define BOOST_ERROR(description) std::cerr << description; std::cerr << "\n";
+
+
+//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
+//
+// Uncomment to print out the complete set of diagnostic messages for the different test cases
+//
+//#define CHECK_EQUAL(description, a, b) if (a != b) {std::cerr << "\n\nError: " << \
+// description << "\n Expecting\n" << b << "\n Found\n" << a << "\n\n"; } \
+// else {std::cout << description<< "\t" << b << "\n";}
+
+//8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
+
+
+//
+// test exception for each specified command line style, e.g. short dash or config file
+//
+template<typename EXCEPTION>
+void test_each_exception_message(const string& test_description, const vector<const char*>& argv, options_description& desc, int style, string exception_msg, istream& is = cin)
+{
+ if (exception_msg.length() == 0)
+ return;
+ variables_map vm;
+ unsigned argc = argv.size();
+
+
+ try {
+ if (style == -1)
+ store(parse_config_file(is, desc), vm);
+ else
+ store(parse_command_line(argv.size(), argv.data(), desc, style), vm);
+ notify(vm);
+ }
+ catch (EXCEPTION& e)
+ {
+ //cerr << "Correct:\n\t" << e.what() << "\n";
+ CHECK_EQUAL(test_description, e.what(), exception_msg);
+ return;
+ }
+ catch (std::exception& e)
+ {
+ // concatenate argv without boost::algorithm::join
+ string argv_txt;
+ for (unsigned ii = 0; ii < argc - 1; ++ii)
+ argv_txt += argv[ii] + string(" ");
+ if (argc)
+ argv_txt += argv[argc - 1];
+
+ BOOST_ERROR("\n<<" + test_description +
+ string(">>\n Unexpected exception type!\n Actual text =\"") + e.what() +
+ "\"\n argv =\"" + argv_txt +
+ "\"\n Expected text=\"" + exception_msg + "\"\n");
+ return;
+ }
+ BOOST_ERROR(test_description + ": No exception thrown. ");
+}
+
+
+
+
+//
+// test exception messages for all command line styles (unix/long/short/slash/config file)
+//
+// try each command line style in turn
+const int unix_style = command_line_style::unix_style;
+const int short_dash = command_line_style::allow_dash_for_short | command_line_style::allow_short | command_line_style::short_allow_adjacent | command_line_style::allow_sticky;
+const int short_slash = command_line_style::allow_slash_for_short | command_line_style::allow_short | command_line_style::short_allow_adjacent;
+const int long_dash = command_line_style::allow_long | command_line_style::long_allow_adjacent | command_line_style::allow_guessing;
+
+
+
+template<typename EXCEPTION>
+void test_exception_message(const vector<vector<const char*>>& argv,
+ options_description& desc,
+ const string& error_description,
+ const char* expected_message_template[5])
+{
+ string expected_message;
+
+ // unix
+ expected_message = expected_message_template[0];
+ test_each_exception_message<EXCEPTION>(error_description + " -- unix",
+ argv[0], desc, unix_style, expected_message);
+
+ // long dash only
+ expected_message = expected_message_template[1];
+ test_each_exception_message<EXCEPTION>(error_description + " -- long_dash",
+ argv[1], desc, long_dash, expected_message);
+
+
+ // short dash only
+ expected_message = expected_message_template[2];
+ test_each_exception_message<EXCEPTION>(error_description + " -- short_dash",
+ argv[2], desc, short_dash, expected_message);
+
+ // short slash only
+ expected_message = expected_message_template[3];
+ test_each_exception_message<EXCEPTION>(error_description + " -- short_slash",
+ argv[3], desc, short_slash, expected_message);
+
+ // config file only
+ expected_message = expected_message_template[4];
+ if (expected_message.length())
+ {
+ istringstream istrm(argv[4][0]);
+ test_each_exception_message<EXCEPTION>(error_description + " -- config_file",
+ argv[4], desc, -1, expected_message, istrm);
+ }
+
+}
+
+#define VEC_STR_PUSH_BACK(vec, c_array) \
+ vec.push_back(vector<const char*>(c_array, c_array + sizeof(c_array) / sizeof(char*)));
+
+//________________________________________________________________________________________
+//
+// invalid_option_value
+//
+//________________________________________________________________________________________
+void test_invalid_option_value_exception_msg()
+{
+ options_description desc;
+ int required_value;
+ desc.add_options()
+ ("int_option,d", value< int >(), "An option taking an integer")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--int", "A_STRING"}; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/d", "A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "int_option=A_STRING"} ; VEC_STR_PUSH_BACK(argv, argv4);
+
+ const char* expected_msg[5] = {
+ "the argument ('A_STRING') for option '--int_option' is invalid",
+ "the argument ('A_STRING') for option '--int_option' is invalid",
+ "the argument ('A_STRING') for option '-d' is invalid",
+ "the argument ('A_STRING') for option '/d' is invalid",
+ "the argument ('A_STRING') for option 'int_option' is invalid"
+ };
+
+
+ test_exception_message<invalid_option_value>(argv, desc, "invalid_option_value",
+ expected_msg);
+
+
+}
+
+//________________________________________________________________________________________
+//
+// missing_value
+//
+//________________________________________________________________________________________
+void test_missing_value_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,e", value<string>(), "the config file")
+ ("output,o", value<string>(), "the output file")
+ ;
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-e", "-e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--cfgfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-e", "-e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/e", "/e", "output.txt"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { ""} ; VEC_STR_PUSH_BACK(argv, argv4);
+
+ const char* expected_msg[5] = {
+ "the required argument for option '--cfgfile' is missing",
+ "the required argument for option '--cfgfile' is missing",
+ "the required argument for option '-e' is missing",
+ "", // Ignore probable bug in cmdline::finish_option
+ //"the required argument for option '/e' is missing",
+ "",
+ };
+ test_exception_message<invalid_command_line_syntax>(argv, desc,
+ "invalid_syntax::missing_parameter",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// ambiguous_option
+//
+//________________________________________________________________________________________
+void test_ambiguous_option_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile1,c", value<string>()->multitoken(), "the config file")
+ ("cfgfile2,o", value<string>(), "the config file")
+ ("good,g", "good option")
+ ("output,c", value<string>(), "the output file")
+ ("output", value<string>(), "the output file")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-ggc", "file", "-o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--cfgfile", "file", "--cfgfile", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-ggc", "file", "-o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/c", "file", "/o", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "output=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '-c' is ambiguous and matches '--cfgfile1', and '--output'",
+ "option '--cfgfile' is ambiguous and matches '--cfgfile1', and '--cfgfile2'",
+ "option '-c' is ambiguous",
+ "option '/c' is ambiguous",
+ "option 'output' is ambiguous and matches different versions of 'output'",
+ };
+ test_exception_message<ambiguous_option>(argv, desc, "ambiguous_option",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// multiple_occurrences
+//
+//________________________________________________________________________________________
+void test_multiple_occurrences_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<string>(), "the configfile")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-c", "file", "-c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--cfgfi", "file", "--cfgfi", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-c", "file", "-c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/c", "file", "/c", "anotherfile"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "cfgfile=output.txt\ncfgfile=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '--cfgfile' cannot be specified more than once",
+ "option '--cfgfile' cannot be specified more than once",
+ "option '-c' cannot be specified more than once",
+ "option '/c' cannot be specified more than once",
+ "option 'cfgfile' cannot be specified more than once",
+ };
+ test_exception_message<multiple_occurrences>(argv, desc, "multiple_occurrences",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// unknown_option
+//
+//________________________________________________________________________________________
+void test_unknown_option_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("good,g", "good option")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-ggc", "file"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--cfgfile", "file"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-ggc", "file"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/c", "file"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "cfgfile=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "unrecognised option '-ggc'",
+ "unrecognised option '--cfgfile'",
+ "unrecognised option '-ggc'",
+ "unrecognised option '/c'",
+ "unrecognised option 'cfgfile'",
+ };
+ test_exception_message<unknown_option>(argv, desc, "unknown_option", expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// validation_error::invalid_bool_value
+//
+//________________________________________________________________________________________
+void test_invalid_bool_value_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("bool_option,b", value< bool>(), "bool_option")
+ ;
+
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = {"program", "-b", "file"} ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = {"program", "--bool_optio", "file"} ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = {"program", "-b", "file"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = {"program", "/b", "file"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "bool_option=output.txt\n"} ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "the argument ('file') for option '--bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('file') for option '--bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('file') for option '-b' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('file') for option '/b' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ "the argument ('output.txt') for option 'bool_option' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'",
+ };
+ test_exception_message<validation_error>(argv,
+ desc,
+ "validation_error::invalid_bool_value",
+ expected_msg);
+}
+
+
+
+
+//________________________________________________________________________________________
+//
+// validation_error::multiple_values_not_allowed
+//
+//________________________________________________________________________________________
+//
+// Strange exception: sole purpose seems to be catching multitoken() not associated with a vector<string>??
+// This is surely a corner case.
+//
+void test_multiple_values_not_allowed_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<string>()->multitoken(), "the config file")
+ ("good,g", "good option")
+ ("output,o", value<string>()->multitoken(), "the output file")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-c", "file", "c", "-o", "fritz", "hugo" } ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--cfgfil", "file", "c", "--outpu", "fritz", "hugo" } ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-c", "file", "c", "-o", "fritz", "hugo"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/c", "file", "c", "/o", "fritz", "hugo"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "option '--cfgfile' only takes a single argument",
+ "option '--cfgfile' only takes a single argument",
+ "option '-c' only takes a single argument",
+ "option '/c' only takes a single argument",
+ "",
+ };
+ test_exception_message<validation_error>(argv,
+ desc,
+ "validation_error::multiple_values_not_allowed",
+ expected_msg);
+}
+
+//________________________________________________________________________________________
+//
+// validation_error::at_least_one_value_required
+//
+//________________________________________________________________________________________
+//
+// Can't build a test case!!!
+//
+//
+void test_at_least_one_value_required_exception_msg()
+{
+
+ return;
+
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<vector<int>>()->multitoken(), "the config file")
+ ("other,o", value<string>(), "other")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-c" } ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--cfg" } ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-c" , "-o" , "name" } ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/c" } ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "Unexpected exception. the required argument for option '--cfgfile' is missing",
+ "Unexpected exception. the required argument for option '--cfgfile' is missing",
+ "Unexpected exception. the required argument for option '-c' is missing",
+ "Unexpected exception. the required argument for option '/c' is missing",
+ "",
+ };
+ test_exception_message<validation_error>(argv,
+ desc,
+ "validation_error::at_least_one_value_required",
+ expected_msg);
+}
+
+
+//________________________________________________________________________________________
+//
+// required_option
+//
+//________________________________________________________________________________________
+void test_required_option_exception_msg()
+{
+ options_description desc;
+ desc.add_options()
+ ("cfgfile,c", value<string>()->required(), "the config file")
+ ("good,g", "good option")
+ ("output,o", value<string>()->required(), "the output file")
+ ;
+
+ vector<vector<const char*>> argv;
+ const char* argv0[] = { "program", "-g" } ; VEC_STR_PUSH_BACK(argv, argv0);
+ const char* argv1[] = { "program", "--g" } ; VEC_STR_PUSH_BACK(argv, argv1);
+ const char* argv2[] = { "program", "-g"} ; VEC_STR_PUSH_BACK(argv, argv2);
+ const char* argv3[] = { "program", "/g"} ; VEC_STR_PUSH_BACK(argv, argv3);
+ const char* argv4[] = { "" } ; VEC_STR_PUSH_BACK(argv, argv4);
+ const char* expected_msg[5] = {
+ "the option '--cfgfile' is required but missing",
+ "the option '--cfgfile' is required but missing",
+ "the option '-c' is required but missing",
+ "the option '/c' is required but missing",
+ "the option 'cfgfile' is required but missing",
+ };
+ test_exception_message<required_option>(argv,
+ desc,
+ "required_option",
+ expected_msg);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**
+ * Check if this is the expected exception with the right message is being thrown inside
+ * func
+*/
+template <typename EXCEPTION, typename FUNC>
+void test_exception(const string& test_name, const string& exception_txt, FUNC func)
+{
+
+ try {
+ options_description desc;
+ variables_map vm;
+ func(desc, vm);
+ }
+ catch (EXCEPTION& e)
+ {
+ CHECK_EQUAL(test_name, e.what(), exception_txt);
+ return;
+ }
+ catch (std::exception& e)
+ {
+ BOOST_ERROR(string(test_name + ":\nUnexpected exception. ") + e.what() +
+ "\nExpected text:\n" + exception_txt + "\n\n");
+ return;
+ }
+ BOOST_ERROR(test_name + ": No exception thrown. ");
+}
+
+
+
+//________________________________________________________________________________________
+//
+// check_reading_file
+//
+//________________________________________________________________________________________
+void check_reading_file(options_description& desc, variables_map& vm)
+{
+ desc.add_options()
+ ("output,o", value<string>(), "the output file");
+
+ const char* file_name = "no_such_file";
+ store(parse_config_file<char>(file_name, desc, true), vm);
+
+}
+
+
+//________________________________________________________________________________________
+//
+// config_file_wildcard
+//
+//________________________________________________________________________________________
+void config_file_wildcard(options_description& desc, variables_map& vm)
+{
+ desc.add_options()
+ ("outpu*", value<string>(), "the output file1")
+ ("outp*", value<string>(), "the output file2")
+ ;
+ istringstream is("output1=whichone\noutput2=whichone\n");
+ store(parse_config_file(is, desc), vm);
+}
+
+//________________________________________________________________________________________
+//
+// invalid_syntax::unrecognized_line
+//
+//________________________________________________________________________________________
+void unrecognized_line(options_description& desc, variables_map& vm)
+{
+ istringstream is("funny wierd line\n");
+ store(parse_config_file(is, desc), vm);
+}
+
+//________________________________________________________________________________________
+//
+// abbreviated_options_in_config_file
+//
+//________________________________________________________________________________________
+void abbreviated_options_in_config_file(options_description& desc, variables_map& vm)
+{
+ desc.add_options()(",o", value<string>(), "the output file");
+ istringstream is("o=output.txt\n");
+ store(parse_config_file(is, desc), vm);
+}
+
+
+//________________________________________________________________________________________
+//
+// too_many_positional_options
+//
+//________________________________________________________________________________________
+void too_many_positional_options(options_description& desc, variables_map& vm)
+{
+ const char* argv[] = {"program", "1", "2", "3"};
+ positional_options_description positional_args;
+ positional_args.add("two_positional_arguments", 2);
+ store(command_line_parser(4, argv).options(desc).positional(positional_args).run(), vm);
+}
+
+
+//________________________________________________________________________________________
+//
+// invalid_command_line_style
+//
+//________________________________________________________________________________________
+
+void test_invalid_command_line_style_exception_msg()
+{
+ string test_name = "invalid_command_line_style";
+ using namespace command_line_style;
+ unsigned basic_unix_style = (allow_short
+ | allow_long
+ | allow_sticky | allow_guessing
+ | allow_dash_for_short);
+ options_description desc;
+ desc.add_options()("output,o", value<string>(), "the output file");
+
+ vector<int> invalid_styles;
+ invalid_styles.push_back(allow_short | short_allow_adjacent);
+ invalid_styles.push_back(allow_short | allow_dash_for_short);
+ invalid_styles.push_back(allow_long);
+ vector<string> invalid_diagnostics;
+ invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
+ "or other of command_line_style::allow_slash_for_short "
+ "(slashes) or command_line_style::allow_dash_for_short "
+ "(dashes) for short options.");
+ invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
+ "or other of 'command_line_style::short_allow_next' "
+ "(whitespace separated arguments) or "
+ "command_line_style::short_allow_adjacent ('=' "
+ "separated arguments) for short options.");
+ invalid_diagnostics.push_back("boost::program_options misconfiguration: choose one "
+ "or other of 'command_line_style::long_allow_next' "
+ "(whitespace separated arguments) or "
+ "command_line_style::long_allow_adjacent ('=' "
+ "separated arguments) for long options.");
+
+
+ const char* argv[] = {"program"};
+ variables_map vm;
+ for (unsigned ii = 0; ii < 3; ++ii)
+ {
+ bool exception_thrown = false;
+ try
+ {
+ store(parse_command_line(1, argv, desc, invalid_styles[ii]), vm);
+ }
+ catch (invalid_command_line_style& e)
+ {
+ string error_msg("arguments are not allowed for unabbreviated option names");
+ CHECK_EQUAL(test_name, e.what(), invalid_diagnostics[ii]);
+ exception_thrown = true;
+ }
+ catch (std::exception& e)
+ {
+ BOOST_ERROR(string(test_name + ":\nUnexpected exception. ") + e.what() +
+ "\nExpected text:\n" + invalid_diagnostics[ii] + "\n");
+ exception_thrown = true;
+ }
+ if (!exception_thrown)
+ {
+ BOOST_ERROR(test_name << ": No exception thrown. ");
+ }
+ }
+}
+
+
+int main(int /*ac*/, char** /*av*/)
+{
+ test_ambiguous_option_exception_msg();
+ test_unknown_option_exception_msg();
+ test_multiple_occurrences_exception_msg();
+ test_missing_value_exception_msg();
+ test_invalid_option_value_exception_msg();
+ test_invalid_bool_value_exception_msg();
+ test_multiple_values_not_allowed_exception_msg();
+ test_required_option_exception_msg();
+ test_at_least_one_value_required_exception_msg();
+
+
+ string test_name;
+ string expected_message;
+
+
+ // check_reading_file
+ test_name = "check_reading_file";
+ expected_message = "can not read options configuration file 'no_such_file'";
+ test_exception<reading_file>(test_name, expected_message, check_reading_file);
+
+ // config_file_wildcard
+ test_name = "config_file_wildcard";
+ expected_message = "options 'outpu*' and 'outp*' will both match the same arguments from the configuration file";
+ test_exception<error>(test_name, expected_message, config_file_wildcard);
+
+ // unrecognized_line
+ test_name = "unrecognized_line";
+ expected_message = "the options configuration file contains an invalid line 'funny wierd line'";
+ test_exception<invalid_syntax>(test_name, expected_message, unrecognized_line);
+
+
+ // abbreviated_options_in_config_file
+ test_name = "abbreviated_options_in_config_file";
+ expected_message = "abbreviated option names are not permitted in options configuration files";
+ test_exception<error>(test_name, expected_message, abbreviated_options_in_config_file);
+
+ test_name = "too_many_positional_options";
+ expected_message = "too many positional arguments have been specified on the command line";
+ test_exception<too_many_positional_options_error>(
+ test_name, expected_message, too_many_positional_options);
+
+ test_invalid_command_line_style_exception_msg();
+ return 0;
+}
+
+
+

Modified: trunk/libs/program_options/test/required_test.cpp
==============================================================================
--- trunk/libs/program_options/test/required_test.cpp (original)
+++ trunk/libs/program_options/test/required_test.cpp 2012-04-08 04:42:39 EDT (Sun, 08 Apr 2012)
@@ -34,6 +34,7 @@
          notify(vm);
       }
       catch (required_option& e) {
+ BOOST_CHECK_EQUAL(e.what(), string("the option '--cfgfile' is required but missing"));
          throwed = true;
       }
       BOOST_CHECK(throwed);


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