diff --git a/include/boost/program_options/detail/value_semantic.hpp b/include/boost/program_options/detail/value_semantic.hpp index 06c9849..464e306 100644 --- a/include/boost/program_options/detail/value_semantic.hpp +++ b/include/boost/program_options/detail/value_semantic.hpp @@ -55,11 +55,11 @@ namespace boost { namespace program_options { { static std::basic_string empty; if (v.size() > 1) - boost::throw_exception(validation_error("multiple values not allowed")); + boost::throw_exception(validation_error(validation_error::multiple_values_not_allowed)); else if (v.size() == 1) return v.front(); else if (!allow_empty) - boost::throw_exception(validation_error("at least one value required")); + boost::throw_exception(validation_error(validation_error::at_least_one_value_required)); return empty; } diff --git a/include/boost/program_options/errors.hpp b/include/boost/program_options/errors.hpp index 938f8eb..3da1709 100644 --- a/include/boost/program_options/errors.hpp +++ b/include/boost/program_options/errors.hpp @@ -25,17 +25,33 @@ namespace boost { namespace program_options { class BOOST_PROGRAM_OPTIONS_DECL invalid_syntax : public error { public: - invalid_syntax(const std::string& tokens, const std::string& msg) - : error(std::string(msg).append(" in '").append(tokens).append("'")), - tokens(tokens), msg(msg) - {} + 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); // gcc says that throw specification on dtor is loosened // without this line ~invalid_syntax() throw() {} + + kind_t kind() const; + + protected: + /** Used to convert kind_t to a related error text */ + static std::string error_message(kind_t kind); + private: // TODO: copy ctor might throw - std::string tokens, msg; + std::string m_tokens; + + kind_t m_kind; }; /** Class thrown when option name is not recognized. */ @@ -50,12 +66,10 @@ namespace boost { namespace program_options { // without this line ~unknown_option() throw() {} - const std::string& get_option_name() const throw() - { - return m_option_name; - } + const std::string& get_option_name() const throw(); + private: - std::string m_option_name; + std::string m_option_name; }; /** Class thrown when there's ambiguity amoung several possible options. */ @@ -63,30 +77,35 @@ namespace boost { namespace program_options { public: ambiguous_option(const std::string& name, const std::vector& alternatives) - : error(std::string("ambiguous option ").append(name)), - alternatives(alternatives) + : error(std::string("ambiguous option ").append(name)) + , alternatives(alternatives) + , m_option_name(name) {} ~ambiguous_option() throw() {} + + const std::string& get_option_name() const throw(); + private: // TODO: copy ctor might throw std::vector 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 { public: - multiple_values(const std::string& what) - : error(what), m_option_name() {} + 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() - { - return m_option_name; - } + const std::string& get_option_name() const throw(); + private: std::string m_option_name; // The name of the option which // caused the exception. @@ -97,16 +116,15 @@ namespace boost { namespace program_options { them all. */ class BOOST_PROGRAM_OPTIONS_DECL multiple_occurrences : public error { public: - multiple_occurrences(const std::string& what) - : error(what), m_option_name() {} + 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() - { - return m_option_name; - } + const std::string& get_option_name() const throw(); private: std::string m_option_name; // The name of the option which @@ -116,22 +134,40 @@ namespace boost { namespace program_options { /** Class thrown when value of option is incorrect. */ class BOOST_PROGRAM_OPTIONS_DECL validation_error : public error { public: - validation_error(const std::string& what) : error(what) {} + enum kind_t { + multiple_values_not_allowed = 30, + at_least_one_value_required, + invalid_bool_value, + invalid_option_value, + invalid_option + }; + + validation_error(kind_t kind, + const std::string& option_value = "", + const std::string& option_name = ""); + ~validation_error() throw() {} + void set_option_name(const std::string& option); - const std::string& get_option_name() const throw() - { - return m_option_name; - } + 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: - mutable std::string m_message; // For on-demand formatting in 'what' + 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 BOOST_PROGRAM_OPTIONS_DECL invalid_option_value : public validation_error { @@ -142,39 +178,23 @@ namespace boost { namespace program_options { #endif }; - /** Class thrown when there are too many positional options. */ + /** 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: - too_many_positional_options_error(const std::string& what) - : error(what) {} - }; - - /** Class thrown when there are too few positional options. */ - class BOOST_PROGRAM_OPTIONS_DECL too_few_positional_options_error : public error { - public: - too_few_positional_options_error(const std::string& what) - : error(what) {} + too_many_positional_options_error() + : error("too many positional options") + {} }; + /** Class thrown when there are syntax errors in given command line */ class BOOST_PROGRAM_OPTIONS_DECL invalid_command_line_syntax : public invalid_syntax { public: - enum kind_t { - long_not_allowed = 30, - long_adjacent_not_allowed, - short_adjacent_not_allowed, - empty_adjacent_parameter, - missing_parameter, - extra_parameter - }; - invalid_command_line_syntax(const std::string& tokens, kind_t kind); - kind_t kind() const; - protected: - static std::string error_message(kind_t kind); - private: - kind_t m_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) diff --git a/src/cmdline.cpp b/src/cmdline.cpp index 2d90bae..8f8800f 100644 --- a/src/cmdline.cpp +++ b/src/cmdline.cpp @@ -34,13 +34,15 @@ namespace boost { namespace program_options { using namespace std; using namespace boost::program_options::command_line_style; - invalid_command_line_syntax:: - invalid_command_line_syntax(const std::string& tokens, kind_t kind) - : invalid_syntax(tokens, error_message(kind)), m_kind(kind) + invalid_syntax:: + invalid_syntax(const std::string& tokens, kind_t kind) + : error(error_message(kind).append(" in '").append(tokens).append("'")) + , m_tokens(tokens) + , m_kind(kind) {} - + std::string - invalid_command_line_syntax::error_message(kind_t kind) + invalid_syntax::error_message(kind_t kind) { // Initially, store the message in 'const char*' variable, // to avoid conversion to std::string in all cases. @@ -65,18 +67,25 @@ namespace boost { namespace program_options { case extra_parameter: msg = "extra parameter"; break; + case unrecognized_line: + msg = "unrecognized line"; + break; default: msg = "unknown error"; } return msg; } - invalid_command_line_syntax::kind_t - invalid_command_line_syntax::kind() const + invalid_syntax::kind_t + invalid_syntax::kind() const { return m_kind; } + invalid_command_line_syntax:: + invalid_command_line_syntax(const std::string& tokens, kind_t kind) + : invalid_syntax(tokens, kind) + {} }} @@ -326,8 +335,7 @@ namespace boost { namespace program_options { namespace detail { if (opt.position_key != -1) { if (position >= m_positional->max_total_count()) { - boost::throw_exception(too_many_positional_options_error( - "too many positional options")); + boost::throw_exception(too_many_positional_options_error()); } opt.string_key = m_positional->name_for_position(position); ++position; diff --git a/src/config_file.cpp b/src/config_file.cpp index 048c010..434ad10 100644 --- a/src/config_file.cpp +++ b/src/config_file.cpp @@ -114,7 +114,7 @@ namespace boost { namespace program_options { namespace detail { break; } else { - boost::throw_exception(invalid_syntax(s, "unrecognized line")); + boost::throw_exception(invalid_syntax(s, invalid_syntax::unrecognized_line)); } } } diff --git a/src/value_semantic.cpp b/src/value_semantic.cpp index dc07a4e..d24a09f 100644 --- a/src/value_semantic.cpp +++ b/src/value_semantic.cpp @@ -97,9 +97,9 @@ namespace boost { namespace program_options { { if (!value_store.empty()) boost::throw_exception( - multiple_occurrences("multiple_occurrences")); + multiple_occurrences()); if (new_tokens.size() > 1) - boost::throw_exception(multiple_values("multiple_values")); + boost::throw_exception(multiple_values()); value_store = new_tokens.empty() ? std::string("") : new_tokens.front(); } @@ -139,8 +139,7 @@ namespace boost { namespace program_options { else if (s == "off" || s == "no" || s == "0" || s == "false") v = any(false); else - boost::throw_exception(validation_error( - "'" + s + "' doesn't look like a bool value.")); + boost::throw_exception(validation_error(validation_error::invalid_bool_value, s)); } // This is blatant copy-paste. However, templating this will cause a problem, @@ -162,7 +161,7 @@ namespace boost { namespace program_options { else if (s == L"off" || s == L"no" || s == L"0" || s == L"false") v = any(false); else - boost::throw_exception(validation_error("invalid bool value")); + boost::throw_exception(validation_error(validation_error::invalid_bool_value)); } #endif BOOST_PROGRAM_OPTIONS_DECL @@ -188,19 +187,17 @@ namespace boost { namespace program_options { { if (!value.empty()) boost::throw_exception( - multiple_occurrences("multiple_occurrences")); + multiple_occurrences()); } } invalid_option_value:: invalid_option_value(const std::string& bad_value) - : validation_error(string("invalid option value '") - .append(bad_value).append("'")) + : validation_error(validation_error::invalid_option_value, bad_value) {} #ifndef BOOST_NO_STD_WSTRING - namespace { std::string convert_value(const std::wstring& s) @@ -216,43 +213,111 @@ namespace boost { namespace program_options { invalid_option_value:: invalid_option_value(const std::wstring& bad_value) - : validation_error(string("invalid option value '") - .append(convert_value(bad_value)) - .append("'")) + : validation_error(validation_error::invalid_option_value, convert_value(bad_value)) {} #endif + const std::string& + unknown_option::get_option_name() const throw() + { + return m_option_name; + } - void multiple_values::set_option_name(const std::string& option_name) + const std::string& + ambiguous_option::get_option_name() const throw() + { + return m_option_name; + } + + void + multiple_values::set_option_name(const std::string& option_name) { m_option_name = option_name; } - void multiple_occurrences::set_option_name(const std::string& option_name) + const std::string& + multiple_values::get_option_name() const throw() + { + return m_option_name; + } + + void + multiple_occurrences::set_option_name(const std::string& option_name) { m_option_name = option_name; } - void validation_error::set_option_name(const std::string& option_name) + const std::string& + multiple_occurrences::get_option_name() const throw() + { + 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("'")); + } + } + + void + validation_error::set_option_name(const std::string& option_name) { m_option_name = option_name; } - const char* validation_error::what() const throw() + const std::string& + validation_error::get_option_name() const throw() + { + return m_option_name; + } + + std::string + validation_error::error_message(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"; + break; + case invalid_option_value: + msg = "invalid option value"; + break; + case invalid_option: + msg = "invalid option"; + break; + default: + msg = "unknown error"; + } + return msg; + } + + const char* + validation_error::what() const throw() { if (!m_option_name.empty()) { m_message = "in option '" + m_option_name + "': " - + logic_error::what(); - return m_message.c_str(); - - } - else - { - return logic_error::what(); + + error_message(m_kind); } + return m_message.c_str(); } - - - }} diff --git a/test/exception_test.cpp b/test/exception_test.cpp index 33470ea..39de749 100644 --- a/test/exception_test.cpp +++ b/test/exception_test.cpp @@ -92,7 +92,7 @@ void test_multiple_occurrences() catch (multiple_occurrences& e) { BOOST_CHECK_EQUAL(e.get_option_name(), "cfgfile"); - BOOST_CHECK_EQUAL(string(e.what()), "multiple_occurrences"); + BOOST_CHECK_EQUAL(string(e.what()), "multiple occurrences"); } }