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