|
Boost-Commit : |
Subject: [Boost-commit] svn:boost r77829 - in branches/release: boost/program_options boost/program_options/detail libs/program_options libs/program_options/build libs/program_options/doc libs/program_options/example libs/program_options/src libs/program_options/test
From: ghost_at_[hidden]
Date: 2012-04-08 06:02:29
Author: vladimir_prus
Date: 2012-04-08 06:02:26 EDT (Sun, 08 Apr 2012)
New Revision: 77829
URL: http://svn.boost.org/trac/boost/changeset/77829
Log:
Merge from trunk.
Added:
branches/release/libs/program_options/test/exception_txt_test.cpp
- copied unchanged from r77828, /trunk/libs/program_options/test/exception_txt_test.cpp
Properties modified:
branches/release/boost/program_options/ (props changed)
branches/release/boost/program_options/detail/parsers.hpp (contents, props changed)
branches/release/boost/program_options/parsers.hpp (contents, props changed)
branches/release/libs/program_options/ (props changed)
branches/release/libs/program_options/test/parsers_test.cpp (props changed)
Text files modified:
branches/release/boost/program_options/detail/cmdline.hpp | 12 +
branches/release/boost/program_options/detail/parsers.hpp | 6
branches/release/boost/program_options/errors.hpp | 454 +++++++++++++++++++++++++++------------
branches/release/boost/program_options/options_description.hpp | 10
branches/release/boost/program_options/parsers.hpp | 30 ++
branches/release/boost/program_options/value_semantic.hpp | 10
branches/release/boost/program_options/variables_map.hpp | 13
branches/release/libs/program_options/build/Jamfile.v2 | 2
branches/release/libs/program_options/doc/overview.xml | 2
branches/release/libs/program_options/example/first.cpp | 6
branches/release/libs/program_options/example/multiple_sources.cpp | 2
branches/release/libs/program_options/example/options_description.cpp | 2
branches/release/libs/program_options/src/cmdline.cpp | 324 ++++++++++++++++-----------
branches/release/libs/program_options/src/config_file.cpp | 6
branches/release/libs/program_options/src/options_description.cpp | 46 +++
branches/release/libs/program_options/src/parsers.cpp | 10
branches/release/libs/program_options/src/value_semantic.cpp | 285 ++++++++++++++++--------
branches/release/libs/program_options/src/variables_map.cpp | 130 ++++++----
branches/release/libs/program_options/test/cmdline_test.cpp | 9
branches/release/libs/program_options/test/exception_test.cpp | 12
branches/release/libs/program_options/test/program_options_size_test.py | 4
branches/release/libs/program_options/test/required_test.cpp | 1
22 files changed, 916 insertions(+), 460 deletions(-)
Modified: branches/release/boost/program_options/detail/cmdline.hpp
==============================================================================
--- branches/release/boost/program_options/detail/cmdline.hpp (original)
+++ branches/release/boost/program_options/detail/cmdline.hpp 2012-04-08 06:02:26 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: branches/release/boost/program_options/detail/parsers.hpp
==============================================================================
--- branches/release/boost/program_options/detail/parsers.hpp (original)
+++ branches/release/boost/program_options/detail/parsers.hpp 2012-04-08 06:02:26 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: branches/release/boost/program_options/errors.hpp
==============================================================================
--- branches/release/boost/program_options/errors.hpp (original)
+++ branches/release/boost/program_options/errors.hpp 2012-04-08 06:02:26 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: branches/release/boost/program_options/options_description.hpp
==============================================================================
--- branches/release/boost/program_options/options_description.hpp (original)
+++ branches/release/boost/program_options/options_description.hpp 2012-04-08 06:02:26 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: branches/release/boost/program_options/parsers.hpp
==============================================================================
--- branches/release/boost/program_options/parsers.hpp (original)
+++ branches/release/boost/program_options/parsers.hpp 2012-04-08 06:02:26 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;
@@ -260,6 +282,10 @@
}}
+#if defined(BOOST_MSVC)
+# pragma warning (pop)
+#endif
+
#undef DECL
#include "boost/program_options/detail/parsers.hpp"
Modified: branches/release/boost/program_options/value_semantic.hpp
==============================================================================
--- branches/release/boost/program_options/value_semantic.hpp (original)
+++ branches/release/boost/program_options/value_semantic.hpp 2012-04-08 06:02:26 EDT (Sun, 08 Apr 2012)
@@ -261,13 +261,21 @@
return this;
}
- /** Specifies that the value can span multiple tokens. */
+ /** Specifies that the value can span multiple tokens.
+ */
typed_value* multitoken()
{
m_multitoken = true;
return this;
}
+ /** Specifies that no tokens may be provided as the value of
+ this option, which means that only presense of the option
+ is significant. For such option to be useful, either the
+ 'validate' function should be specialized, or the
+ 'implicit_value' method should be also used. In most
+ cases, you can use the 'bool_switch' function instead of
+ using this method. */
typed_value* zero_tokens()
{
m_zero_tokens = true;
Modified: branches/release/boost/program_options/variables_map.hpp
==============================================================================
--- branches/release/boost/program_options/variables_map.hpp (original)
+++ branches/release/boost/program_options/variables_map.hpp 2012-04-08 06:02:26 EDT (Sun, 08 Apr 2012)
@@ -153,6 +153,9 @@
// Resolve conflict between inherited operators.
const variable_value& operator[](const std::string& name) const
{ return abstract_variables_map::operator[](name); }
+
+ // Override to clear some extra fields.
+ void clear();
void notify();
@@ -171,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;
};
@@ -208,4 +213,8 @@
}}
+#if defined(BOOST_MSVC)
+# pragma warning (pop)
+#endif
+
#endif
Modified: branches/release/libs/program_options/build/Jamfile.v2
==============================================================================
--- branches/release/libs/program_options/build/Jamfile.v2 (original)
+++ branches/release/libs/program_options/build/Jamfile.v2 2012-04-08 06:02:26 EDT (Sun, 08 Apr 2012)
@@ -12,6 +12,8 @@
lib boost_program_options
: $(SOURCES).cpp
: <link>shared:<define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1 # tell source we're building dll's
+ # See https://svn.boost.org/trac/boost/ticket/5049
+ <target-os>hpux,<toolset>gcc:<define>_INCLUDE_STDC__SOURCE_199901
:
: <link>shared:<define>BOOST_PROGRAM_OPTIONS_DYN_LINK=1
;
Modified: branches/release/libs/program_options/doc/overview.xml
==============================================================================
--- branches/release/libs/program_options/doc/overview.xml (original)
+++ branches/release/libs/program_options/doc/overview.xml 2012-04-08 06:02:26 EDT (Sun, 08 Apr 2012)
@@ -181,7 +181,7 @@
desc.add_options()
("help", "produce help message")
("compression", value<string>(), "compression level")
- ("verbose", value<string>()->zero_tokens(), "verbosity level")
+ ("verbose", value<string>()->implicit_value("0"), "verbosity level")
("email", value<string>()->multitoken(), "email to send to")
;
</programlisting>
Modified: branches/release/libs/program_options/example/first.cpp
==============================================================================
--- branches/release/libs/program_options/example/first.cpp (original)
+++ branches/release/libs/program_options/example/first.cpp 2012-04-08 06:02:26 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;
@@ -29,12 +29,12 @@
if (vm.count("help")) {
cout << desc << "\n";
- return 1;
+ return 0;
}
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: branches/release/libs/program_options/example/multiple_sources.cpp
==============================================================================
--- branches/release/libs/program_options/example/multiple_sources.cpp (original)
+++ branches/release/libs/program_options/example/multiple_sources.cpp 2012-04-08 06:02:26 EDT (Sun, 08 Apr 2012)
@@ -18,7 +18,7 @@
template<class T>
ostream& operator<<(ostream& os, const vector<T>& v)
{
- copy(v.begin(), v.end(), ostream_iterator<T>(cout, " "));
+ copy(v.begin(), v.end(), ostream_iterator<T>(os, " "));
return os;
}
Modified: branches/release/libs/program_options/example/options_description.cpp
==============================================================================
--- branches/release/libs/program_options/example/options_description.cpp (original)
+++ branches/release/libs/program_options/example/options_description.cpp 2012-04-08 06:02:26 EDT (Sun, 08 Apr 2012)
@@ -18,7 +18,7 @@
template<class T>
ostream& operator<<(ostream& os, const vector<T>& v)
{
- copy(v.begin(), v.end(), ostream_iterator<T>(cout, " "));
+ copy(v.begin(), v.end(), ostream_iterator<T>(os, " "));
return os;
}
Modified: branches/release/libs/program_options/src/cmdline.cpp
==============================================================================
--- branches/release/libs/program_options/src/cmdline.cpp (original)
+++ branches/release/libs/program_options/src/cmdline.cpp 2012-04-08 06:02:26 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()
@@ -277,7 +284,7 @@
}
/* If an key option is followed by a positional option,
- can can consume more tokens (e.g. it's multitoke option),
+ can can consume more tokens (e.g. it's multitoken option),
give those tokens to it. */
vector<option> result2;
for (unsigned i = 0; i < result.size(); ++i)
@@ -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: branches/release/libs/program_options/src/config_file.cpp
==============================================================================
--- branches/release/libs/program_options/src/config_file.cpp (original)
+++ branches/release/libs/program_options/src/config_file.cpp 2012-04-08 06:02:26 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: branches/release/libs/program_options/src/options_description.cpp
==============================================================================
--- branches/release/libs/program_options/src/options_description.cpp (original)
+++ branches/release/libs/program_options/src/options_description.cpp 2012-04-08 06:02:26 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
{
@@ -174,10 +199,13 @@
option_description::format_name() const
{
if (!m_short_name.empty())
- return string(m_short_name).append(" [ --").
- append(m_long_name).append(" ]");
- else
- return string("--").append(m_long_name);
+ {
+ return m_long_name.empty()
+ ? m_short_name
+ : string(m_short_name).append(" [ --").
+ append(m_long_name).append(" ]");
+ }
+ return string("--").append(m_long_name);
}
std::string
@@ -289,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;
}
@@ -337,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.
@@ -346,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();
}
@@ -396,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: branches/release/libs/program_options/src/parsers.cpp
==============================================================================
--- branches/release/libs/program_options/src/parsers.cpp (original)
+++ branches/release/libs/program_options/src/parsers.cpp 2012-04-08 06:02:26 EDT (Sun, 08 Apr 2012)
@@ -45,7 +45,10 @@
// See: http://article.gmane.org/gmane.comp.lib.boost.devel/103843
// See: http://lists.gnu.org/archive/html/bug-guile/2004-01/msg00013.html
#if defined(__APPLE__) && defined(__DYNAMIC__)
-#include <crt_externs.h>
+// The proper include for this is crt_externs.h, however it's not
+// available on iOS. The right replacement is not known. See
+// https://svn.boost.org/trac/boost/ticket/5053
+extern "C" { extern char ***_NSGetEnviron(void); }
#define environ (*_NSGetEnviron())
#else
#if defined(__MWERKS__)
@@ -85,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]));
@@ -107,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: branches/release/libs/program_options/src/value_semantic.cpp
==============================================================================
--- branches/release/libs/program_options/src/value_semantic.cpp (original)
+++ branches/release/libs/program_options/src/value_semantic.cpp 2012-04-08 06:02:26 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: branches/release/libs/program_options/src/variables_map.cpp
==============================================================================
--- branches/release/libs/program_options/src/variables_map.cpp (original)
+++ branches/release/libs/program_options/src/variables_map.cpp 2012-04-08 06:02:26 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;
}
}
}
@@ -182,6 +192,13 @@
: abstract_variables_map(next)
{}
+ void variables_map::clear()
+ {
+ std::map<std::string, variable_value>::clear();
+ m_final.clear();
+ m_required.clear();
+ }
+
const variable_value&
variables_map::get(const std::string& name) const
{
@@ -197,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: branches/release/libs/program_options/test/cmdline_test.cpp
==============================================================================
--- branches/release/libs/program_options/test/cmdline_test.cpp (original)
+++ branches/release/libs/program_options/test/cmdline_test.cpp 2012-04-08 06:02:26 EDT (Sun, 08 Apr 2012)
@@ -378,6 +378,15 @@
{0, 0, 0}
};
test_cmdline("opt123 opt56 foo,f=", style, test_cases1);
+
+ test_case test_cases2[] = {
+ {"--fname file --fname2 file2", s_success, "fname: file fname2: file2"},
+ {"--fnam file --fnam file2", s_ambiguous_option, ""},
+ {"--fnam file --fname2 file2", s_ambiguous_option, ""},
+ {"--fname2 file2 --fnam file", s_ambiguous_option, ""},
+ {0, 0, 0}
+ };
+ test_cmdline("fname fname2", style, test_cases2);
}
void test_arguments()
Modified: branches/release/libs/program_options/test/exception_test.cpp
==============================================================================
--- branches/release/libs/program_options/test/exception_test.cpp (original)
+++ branches/release/libs/program_options/test/exception_test.cpp 2012-04-08 06:02:26 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");
}
}
Modified: branches/release/libs/program_options/test/program_options_size_test.py
==============================================================================
--- branches/release/libs/program_options/test/program_options_size_test.py (original)
+++ branches/release/libs/program_options/test/program_options_size_test.py 2012-04-08 06:02:26 EDT (Sun, 08 Apr 2012)
@@ -21,7 +21,7 @@
f.write("(\"opt%d\", value<int>())\n")
f.write(";\n}\n")
f.close()
- os.system(compiler_command + " -c -save-temps -I /home/ghost/Work/boost-rc program_options_test.cpp")
+ os.system(compiler_command + " -c -save-temps -I /home/ghost/Work/Boost/boost-svn program_options_test.cpp")
nm = os.popen("nm -S program_options_test.o")
for l in nm:
@@ -45,7 +45,7 @@
print "Avarage: ", (last_size-first_size)/(range[-1]-range[0])
if __name__ == '__main__':
- for compiler in [ "g++-3.3 -Os", "g++-3.3 -O3", "g++-3.4 -Os", "g++-3.4 -O3"]:
+ for compiler in [ "g++ -Os", "g++ -O3"]:
print "****", compiler, "****"
run_tests(range(1, 20), compiler)
Modified: branches/release/libs/program_options/test/required_test.cpp
==============================================================================
--- branches/release/libs/program_options/test/required_test.cpp (original)
+++ branches/release/libs/program_options/test/required_test.cpp 2012-04-08 06:02:26 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