1 // Copyright Vladimir Prus 2002-2004. 2 // Distributed under the Boost Software License, Version 1.0. 3 // (See accompanying file LICENSE_1_0.txt 4 // or copy at http://www.boost.org/LICENSE_1_0.txt) 5 6 7 #ifndef BOOST_ERRORS_VP_2003_01_02 8 #define BOOST_ERRORS_VP_2003_01_02 9 10 #include <boost/program_options/config.hpp> 11 12 #include <string> 13 #include <stdexcept> 14 #include <vector> 15 #include <map> 16 17 18 #if defined(BOOST_MSVC) 19 # pragma warning (push) 20 # pragma warning (disable:4275) // non dll-interface class 'std::logic_error' used as base for dll-interface class 'boost::program_options::error' 21 # pragma warning (disable:4251) // class 'std::vector<_Ty>' needs to have dll-interface to be used by clients of class 'boost::program_options::ambiguous_option' 22 #endif 23 24 namespace boost { namespace program_options { 25 strip_prefixes(const std::string & text)26 inline std::string strip_prefixes(const std::string& text) 27 { 28 // "--foo-bar" -> "foo-bar" 29 return text.substr(text.find_first_not_of("-/")); 30 } 31 32 /** Base class for all errors in the library. */ 33 class BOOST_PROGRAM_OPTIONS_DECL error : public std::logic_error { 34 public: error(const std::string & xwhat)35 error(const std::string& xwhat) : std::logic_error(xwhat) {} 36 }; 37 38 39 /** Class thrown when there are too many positional options. 40 This is a programming error. 41 */ 42 class BOOST_PROGRAM_OPTIONS_DECL too_many_positional_options_error : public error { 43 public: too_many_positional_options_error()44 too_many_positional_options_error() 45 : error("too many positional options have been specified on the command line") 46 {} 47 }; 48 49 /** Class thrown when there are programming error related to style */ 50 class BOOST_PROGRAM_OPTIONS_DECL invalid_command_line_style : public error { 51 public: invalid_command_line_style(const std::string & msg)52 invalid_command_line_style(const std::string& msg) 53 : error(msg) 54 {} 55 }; 56 57 /** Class thrown if config file can not be read */ 58 class BOOST_PROGRAM_OPTIONS_DECL reading_file : public error { 59 public: reading_file(const char * filename)60 reading_file(const char* filename) 61 : error(std::string("can not read options configuration file '").append(filename).append("'")) 62 {} 63 }; 64 65 66 /** Base class for most exceptions in the library. 67 * 68 * Substitutes the values for the parameter name 69 * placeholders in the template to create the human 70 * readable error message 71 * 72 * Placeholders are surrounded by % signs: %example% 73 * Poor man's version of boost::format 74 * 75 * If a parameter name is absent, perform default substitutions 76 * instead so ugly placeholders are never left in-place. 77 * 78 * Options are displayed in "canonical" form 79 * This is the most unambiguous form of the 80 * *parsed* option name and would correspond to 81 * option_description::format_name() 82 * i.e. what is shown by print_usage() 83 * 84 * The "canonical" form depends on whether the option is 85 * specified in short or long form, using dashes or slashes 86 * or without a prefix (from a configuration file) 87 * 88 * */ 89 class BOOST_PROGRAM_OPTIONS_DECL error_with_option_name : public error { 90 91 protected: 92 /** can be 93 * 0 = no prefix (config file options) 94 * allow_long 95 * allow_dash_for_short 96 * allow_slash_for_short 97 * allow_long_disguise */ 98 int m_option_style; 99 100 101 /** substitutions 102 * from placeholders to values */ 103 std::map<std::string, std::string> m_substitutions; 104 typedef std::pair<std::string, std::string> string_pair; 105 std::map<std::string, string_pair > m_substitution_defaults; 106 107 public: 108 /** template with placeholders */ 109 std::string m_error_template; 110 111 error_with_option_name(const std::string& template_, 112 const std::string& option_name = "", 113 const std::string& original_token = "", 114 int option_style = 0); 115 116 /** gcc says that throw specification on dtor is loosened 117 * without this line 118 * */ ~error_with_option_name()119 ~error_with_option_name() throw() {} 120 121 122 //void dump() const 123 //{ 124 // std::cerr << "m_substitution_defaults:\n"; 125 // for (std::map<std::string, string_pair>::const_iterator iter = m_substitution_defaults.begin(); 126 // iter != m_substitution_defaults.end(); ++iter) 127 // std::cerr << "\t" << iter->first << ":" << iter->second.first << "=" << iter->second.second << "\n"; 128 // std::cerr << "m_substitutions:\n"; 129 // for (std::map<std::string, std::string>::const_iterator iter = m_substitutions.begin(); 130 // iter != m_substitutions.end(); ++iter) 131 // std::cerr << "\t" << iter->first << "=" << iter->second << "\n"; 132 // std::cerr << "m_error_template:\n"; 133 // std::cerr << "\t" << m_error_template << "\n"; 134 // std::cerr << "canonical_option_prefix:[" << get_canonical_option_prefix() << "]\n"; 135 // std::cerr << "canonical_option_name:[" << get_canonical_option_name() <<"]\n"; 136 // std::cerr << "what:[" << what() << "]\n"; 137 //} 138 139 /** Substitute 140 * parameter_name->value to create the error message from 141 * the error template */ set_substitute(const std::string & parameter_name,const std::string & value)142 void set_substitute(const std::string& parameter_name, const std::string& value) 143 { m_substitutions[parameter_name] = value; } 144 145 /** If the parameter is missing, then make the 146 * from->to substitution instead */ set_substitute_default(const std::string & parameter_name,const std::string & from,const std::string & to)147 void set_substitute_default(const std::string& parameter_name, 148 const std::string& from, 149 const std::string& to) 150 { 151 m_substitution_defaults[parameter_name] = std::make_pair(from, to); 152 } 153 154 155 /** Add context to an exception */ add_context(const std::string & option_name,const std::string & original_token,int option_style)156 void add_context(const std::string& option_name, 157 const std::string& original_token, 158 int option_style) 159 { 160 set_option_name(option_name); 161 set_original_token(original_token); 162 set_prefix(option_style); 163 } 164 set_prefix(int option_style)165 void set_prefix(int option_style) 166 { m_option_style = option_style;} 167 168 /** Overridden in error_with_no_option_name */ set_option_name(const std::string & option_name)169 virtual void set_option_name(const std::string& option_name) 170 { set_substitute("option", option_name);} 171 get_option_name() const172 std::string get_option_name() const throw() 173 { return get_canonical_option_name(); } 174 set_original_token(const std::string & original_token)175 void set_original_token(const std::string& original_token) 176 { set_substitute("original_token", original_token);} 177 178 179 /** Creates the error_message on the fly 180 * Currently a thin wrapper for substitute_placeholders() */ 181 virtual const char* what() const throw(); 182 183 protected: 184 /** Used to hold the error text returned by what() */ 185 mutable std::string m_message; // For on-demand formatting in 'what' 186 187 /** Makes all substitutions using the template */ 188 virtual void substitute_placeholders(const std::string& error_template) const; 189 190 // helper function for substitute_placeholders 191 void replace_token(const std::string& from, const std::string& to) const; 192 193 /** Construct option name in accordance with the appropriate 194 * prefix style: i.e. long dash or short slash etc */ 195 std::string get_canonical_option_name() const; 196 std::string get_canonical_option_prefix() const; 197 }; 198 199 200 /** Class thrown when there are several option values, but 201 user called a method which cannot return them all. */ 202 class BOOST_PROGRAM_OPTIONS_DECL multiple_values : public error_with_option_name { 203 public: multiple_values()204 multiple_values() 205 : error_with_option_name("option '%canonical_option%' only takes a single argument"){} 206 ~multiple_values()207 ~multiple_values() throw() {} 208 }; 209 210 /** Class thrown when there are several occurrences of an 211 option, but user called a method which cannot return 212 them all. */ 213 class BOOST_PROGRAM_OPTIONS_DECL multiple_occurrences : public error_with_option_name { 214 public: multiple_occurrences()215 multiple_occurrences() 216 : error_with_option_name("option '%canonical_option%' cannot be specified more than once"){} 217 ~multiple_occurrences()218 ~multiple_occurrences() throw() {} 219 220 }; 221 222 /** Class thrown when a required/mandatory option is missing */ 223 class BOOST_PROGRAM_OPTIONS_DECL required_option : public error_with_option_name { 224 public: 225 // option name is constructed by the option_descriptor and never on the fly required_option(const std::string & option_name)226 required_option(const std::string& option_name) 227 : error_with_option_name("the option '%canonical_option%' is required but missing", "", option_name) 228 { 229 } 230 ~required_option()231 ~required_option() throw() {} 232 }; 233 234 /** Base class of unparsable options, 235 * when the desired option cannot be identified. 236 * 237 * 238 * It makes no sense to have an option name, when we can't match an option to the 239 * parameter 240 * 241 * Having this a part of the error_with_option_name hierachy makes error handling 242 * a lot easier, even if the name indicates some sort of conceptual dissonance! 243 * 244 * */ 245 class BOOST_PROGRAM_OPTIONS_DECL error_with_no_option_name : public error_with_option_name { 246 public: error_with_no_option_name(const std::string & template_,const std::string & original_token="")247 error_with_no_option_name(const std::string& template_, 248 const std::string& original_token = "") 249 : error_with_option_name(template_, "", original_token) 250 { 251 } 252 253 /** Does NOT set option name, because no option name makes sense */ set_option_name(const std::string &)254 virtual void set_option_name(const std::string&) {} 255 ~error_with_no_option_name()256 ~error_with_no_option_name() throw() {} 257 }; 258 259 260 /** Class thrown when option name is not recognized. */ 261 class BOOST_PROGRAM_OPTIONS_DECL unknown_option : public error_with_no_option_name { 262 public: unknown_option(const std::string & original_token="")263 unknown_option(const std::string& original_token = "") 264 : error_with_no_option_name("unrecognised option '%canonical_option%'", original_token) 265 { 266 } 267 ~unknown_option()268 ~unknown_option() throw() {} 269 }; 270 271 272 273 /** Class thrown when there's ambiguity amoung several possible options. */ 274 class BOOST_PROGRAM_OPTIONS_DECL ambiguous_option : public error_with_no_option_name { 275 public: ambiguous_option(const std::vector<std::string> & xalternatives)276 ambiguous_option(const std::vector<std::string>& xalternatives) 277 : error_with_no_option_name("option '%canonical_option%' is ambiguous"), 278 m_alternatives(xalternatives) 279 {} 280 ~ambiguous_option()281 ~ambiguous_option() throw() {} 282 alternatives() const283 const std::vector<std::string>& alternatives() const throw() {return m_alternatives;} 284 285 protected: 286 /** Makes all substitutions using the template */ 287 virtual void substitute_placeholders(const std::string& error_template) const; 288 private: 289 // TODO: copy ctor might throw 290 std::vector<std::string> m_alternatives; 291 }; 292 293 294 /** Class thrown when there's syntax error either for command 295 * line or config file options. See derived children for 296 * concrete classes. */ 297 class BOOST_PROGRAM_OPTIONS_DECL invalid_syntax : public error_with_option_name { 298 public: 299 enum kind_t { 300 long_not_allowed = 30, 301 long_adjacent_not_allowed, 302 short_adjacent_not_allowed, 303 empty_adjacent_parameter, 304 missing_parameter, 305 extra_parameter, 306 unrecognized_line 307 }; 308 invalid_syntax(kind_t kind,const std::string & option_name="",const std::string & original_token="",int option_style=0)309 invalid_syntax(kind_t kind, 310 const std::string& option_name = "", 311 const std::string& original_token = "", 312 int option_style = 0): 313 error_with_option_name(get_template(kind), option_name, original_token, option_style), 314 m_kind(kind) 315 { 316 } 317 ~invalid_syntax()318 ~invalid_syntax() throw() {} 319 kind() const320 kind_t kind() const {return m_kind;} 321 322 /** Convenience functions for backwards compatibility */ tokens() const323 virtual std::string tokens() const {return get_option_name(); } 324 protected: 325 /** Used to convert kind_t to a related error text */ 326 std::string get_template(kind_t kind); 327 kind_t m_kind; 328 }; 329 330 class BOOST_PROGRAM_OPTIONS_DECL invalid_config_file_syntax : public invalid_syntax { 331 public: invalid_config_file_syntax(const std::string & invalid_line,kind_t kind)332 invalid_config_file_syntax(const std::string& invalid_line, kind_t kind): 333 invalid_syntax(kind) 334 { 335 m_substitutions["invalid_line"] = invalid_line; 336 } 337 ~invalid_config_file_syntax()338 ~invalid_config_file_syntax() throw() {} 339 340 /** Convenience functions for backwards compatibility */ tokens() const341 virtual std::string tokens() const {return m_substitutions.find("invalid_line")->second; } 342 }; 343 344 345 /** Class thrown when there are syntax errors in given command line */ 346 class BOOST_PROGRAM_OPTIONS_DECL invalid_command_line_syntax : public invalid_syntax { 347 public: invalid_command_line_syntax(kind_t kind,const std::string & option_name="",const std::string & original_token="",int option_style=0)348 invalid_command_line_syntax(kind_t kind, 349 const std::string& option_name = "", 350 const std::string& original_token = "", 351 int option_style = 0): 352 invalid_syntax(kind, option_name, original_token, option_style) {} ~invalid_command_line_syntax()353 ~invalid_command_line_syntax() throw() {} 354 }; 355 356 357 /** Class thrown when value of option is incorrect. */ 358 class BOOST_PROGRAM_OPTIONS_DECL validation_error : public error_with_option_name { 359 public: 360 enum kind_t { 361 multiple_values_not_allowed = 30, 362 at_least_one_value_required, 363 invalid_bool_value, 364 invalid_option_value, 365 invalid_option 366 }; 367 368 public: validation_error(kind_t kind,const std::string & option_name="",const std::string & original_token="",int option_style=0)369 validation_error(kind_t kind, 370 const std::string& option_name = "", 371 const std::string& original_token = "", 372 int option_style = 0): 373 error_with_option_name(get_template(kind), option_name, original_token, option_style) 374 { 375 } 376 ~validation_error()377 ~validation_error() throw() {} 378 379 protected: 380 /** Used to convert kind_t to a related error text */ 381 std::string get_template(kind_t kind); 382 kind_t m_kind; 383 }; 384 385 /** Class thrown if there is an invalid option value given */ 386 class BOOST_PROGRAM_OPTIONS_DECL invalid_option_value 387 : public validation_error 388 { 389 public: 390 invalid_option_value(const std::string& value); 391 #ifndef BOOST_NO_STD_WSTRING 392 invalid_option_value(const std::wstring& value); 393 #endif 394 }; 395 396 /** Class thrown if there is an invalid bool value given */ 397 class BOOST_PROGRAM_OPTIONS_DECL invalid_bool_value 398 : public validation_error 399 { 400 public: 401 invalid_bool_value(const std::string& value); 402 }; 403 404 405 406 407 408 409 410 }} 411 412 #if defined(BOOST_MSVC) 413 # pragma warning (pop) 414 #endif 415 416 #endif 417