1 /* _________________________________________________________________________ 2 * 3 * UTILIB: A utility library for developing portable C++ codes. 4 * Copyright (c) 2008 Sandia Corporation. 5 * This software is distributed under the BSD License. 6 * Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, 7 * the U.S. Government retains certain rights in this software. 8 * For more information, see the README file in the top UTILIB directory. 9 * _________________________________________________________________________ 10 */ 11 12 #ifndef utilib_OptionParser_h 13 #define utilib_OptionParser_h 14 15 #include <utilib/string_ops.h> 16 #include <utilib/Parameter.h> 17 #include <utilib/SmartHandle.h> 18 19 #ifdef UTILIB_HAVE_TINYXML 20 #include <tinyxml/tinyxml.h> 21 #endif 22 #include <vector> 23 #include <utility> 24 #include <string> 25 26 namespace utilib 27 { 28 29 /// 30 /// This is a simple option parser that is based on the UTILI ParameterSet 31 /// class. This provides a simpler API for setting up command-line options. 32 /// 33 class OptionParser 34 { 35 public: 36 37 /// 38 typedef SmartHandle<Parameter> data_t; 39 40 /// 41 typedef std::vector<std::string> args_t; 42 43 /// Constructor 44 OptionParser(const char* usage_ = 0, const char* description_ = 0) 45 { 46 _help_option = false; 47 _version_option = false; 48 min_num_required_args = 0; 49 required_equals = false; 50 51 if (usage_ != 0) 52 add_usage(usage_); 53 if (description_ != 0) 54 description = description_; 55 add('h', "help", _help_option, "Display usage information"); 56 } 57 58 /// Merge options from an OptionParser object into the current 59 /// OptionParser. This merge ignores arguments, the usage, etc. 60 void merge_options(const OptionParser& options); 61 62 /// 63 unsigned int min_num_required_args; 64 65 /// 66 bool required_equals; 67 68 /// 69 std::string description; 70 71 /// 72 std::string epilog; 73 74 /// add_usage(const std::string & usage_)75 void add_usage(const std::string& usage_) 76 { 77 usage.push_back(usage_); 78 } 79 80 /// version(const std::string & _version)81 void version(const std::string& _version) 82 { 83 std::map<std::string,data_t>::iterator curr = parameters.find(_version); 84 if (curr == parameters.end()) 85 add("version", _version_option, "Display version information"); 86 _version_text = _version; 87 } 88 89 // - Add standard parameters 90 91 /// 92 template <class T> 93 void add(const char& short_name, const std::string& long_name, T& data, const std::string& description_="", Parameter::action_t action = Parameter::store_value) 94 { add_parameter(short_name, long_name, data, description_, action); } 95 96 /// 97 template <class T> 98 void add(const char& short_name, T& data, const std::string& description_="", Parameter::action_t action = Parameter::store_value) 99 { 100 std::string tmp = ""; 101 add_parameter(short_name, tmp, data, description_, action); 102 } 103 104 /// 105 template <class T> 106 void add(const std::string& long_name, T& data, const std::string& description_="", Parameter::action_t action = Parameter::store_value) 107 { 108 char tmp = 0; 109 add_parameter(tmp, long_name, data, description_, action); 110 } 111 112 // - Add list parameters 113 114 /// 115 template <class T> 116 void add(const char& short_name, const std::string& long_name, std::list<T>& data, const std::string& description_="") 117 { add_parameter(short_name, long_name, data, description_, Parameter::append_value); } 118 119 /// 120 template <class T> 121 void add(const char& short_name, std::list<T>& data, const std::string& description_="") 122 { 123 std::string tmp = ""; 124 add_parameter(short_name, tmp, data, description_, Parameter::append_value); 125 } 126 127 /// 128 template <class T> 129 void add(const std::string& long_name, std::list<T>& data, const std::string& description_="") 130 { 131 char tmp = 0; 132 add_parameter(tmp, long_name, data, description_, Parameter::append_value); 133 } 134 135 // - Add boolean parameters 136 137 /// 138 void add(const char& short_name, const std::string& long_name, bool& data, const std::string& description_="", Parameter::action_t action = Parameter::store_true) 139 { add_parameter(short_name, long_name, data, description_, action); } 140 141 /// 142 void add(const char& short_name, bool& data, const std::string& description_="", Parameter::action_t action = Parameter::store_true) 143 { 144 std::string tmp = ""; 145 add_parameter(short_name, tmp, data, description_, action); 146 } 147 148 /// 149 void add(const std::string& long_name, bool& data, const std::string& description_="", Parameter::action_t action = Parameter::store_true) 150 { 151 char tmp = 0; 152 add_parameter(tmp, long_name, data, description_, action); 153 } 154 155 /// 156 void categorize(const std::string& name, const std::string& category); 157 158 /// categorize(char short_name,const std::string & category)159 void categorize(char short_name, const std::string& category) 160 { 161 std::string tmp; 162 tmp += short_name; 163 categorize(tmp,category); 164 } 165 166 /// 167 template <class Type, class FuncT> validate(const std::string & name_,const Type &,FuncT fn)168 void validate(const std::string& name_, const Type&, FuncT fn) 169 { 170 std::string name = standardize(name_); 171 bool posix = name.size() == 1; 172 Parameter& param = get_param(name.c_str(),posix); 173 param.validator = new ParameterValidator<Type, FuncT>(fn); 174 } 175 176 /// Disable parameter, but don't delete it from 177 void disable(std::string name); 178 179 /// Enable a parameter that was previously disabled 180 void enable(std::string name); 181 182 /// Delete parameter 183 void remove(std::string name); 184 185 /// 186 bool initialized(const std::string& name); 187 188 #ifdef UTILIB_HAVE_TINYXML 189 /// 190 void process_xml(TiXmlElement* elt, bool describe=false); 191 #endif 192 193 /// 194 void write(std::ostream& os, bool categorized=true) const 195 { 196 std::set<std::string> tmp; 197 write(os, tmp, categorized); 198 } 199 200 /// 201 void write(std::ostream& os, const std::set<std::string>& categories_requested, bool categorized=true) const; 202 203 /// 204 void write_xml(std::ostream& os) const; 205 206 /// 207 void write_values(std::ostream& os, const std::string& opt_label="") const; 208 209 /// 210 void write_values_xml(std::ostream& os) const; 211 212 /// write_parameters(std::ostream & os)213 void write_parameters(std::ostream& os) const 214 { 215 std::string indent = " "; 216 write_parameter_set(os, parameter_data, indent); 217 } 218 219 /// print_version(std::ostream & os)220 void print_version(std::ostream& os) const 221 { wordwrap_printline(os, _version_text, ""); } 222 223 /// add_argument(const std::string & name_,const std::string & description_)224 void add_argument(const std::string& name_, const std::string& description_) 225 { 226 std::string name = standardize(name_); 227 arg_definitions.push_back(std::pair<std::string, std::string>(name, description_)); 228 } 229 230 /// 231 args_t& parse_args(int argc, char* argv[]); 232 233 /// 234 void set_parameter(std::string name, Any value); 235 236 /// 237 void set_parameter(std::string name, const std::string& value); 238 239 /// set_parameter(std::string name,const char * const & value)240 void set_parameter(std::string name, const char * const &value) 241 { 242 if ( value == NULL ) 243 { 244 //EXCEPTION_MNGR(std::logic_error, "OptionParser::set_parameter(): " 245 // "called with NULL char*"); 246 set_parameter(name, Any(false)); 247 return; 248 } 249 std::string tmp = value; 250 set_parameter(name,tmp); 251 } 252 253 /// returns 0 if DNE, 1 if enabled, -1 if disabled 254 int has_parameter(std::string name); 255 256 /// args()257 args_t& args() 258 { return processed_args; } 259 260 /// args()261 const args_t& args() const 262 { return processed_args; } 263 264 /// 265 void alias(const string& name, const string& alias); 266 267 /// help_option()268 bool help_option() const 269 { return _help_option; } 270 271 /// version_option()272 bool version_option() const 273 { return _version_option; } 274 275 protected: 276 277 /// 278 template <class T> add_parameter(const char & short_name,const std::string & long_name_,T & data,const std::string & description_,Parameter::action_t action)279 void add_parameter(const char& short_name, const std::string& long_name_, T& data, const std::string& description_, Parameter::action_t action) 280 { 281 std::string long_name = standardize(long_name_); 282 if (long_name != "") 283 { 284 std::map<std::string,data_t>::iterator curr = parameters.find(long_name); 285 if (curr != parameters.end()) 286 EXCEPTION_MNGR(runtime_error, 287 "OptionParser::add_parameter - parameter \"" << long_name << "\" already exists!"); 288 if (long_name.size() == 1) { 289 EXCEPTION_MNGR(runtime_error, "OptionParser::add_parameter - cannot specify a non-posix option with a single-character name: " << long_name); 290 } 291 } 292 if (short_name != 0) 293 { 294 std::map<char,data_t>::iterator curr = posix_parameters.find(short_name); 295 if (curr != posix_parameters.end()) 296 EXCEPTION_MNGR(runtime_error, 297 "OptionParser::add_parameter - posix parameter \"" << short_name << "\" already exists!"); 298 } 299 300 std::ostringstream buf; 301 buf << data; 302 data_t param_handle(true); 303 Parameter& param = param_handle(); 304 param.name = long_name.c_str(); 305 param.short_name = short_name; 306 param.setup_comp_name(); 307 param.initialize(data); 308 param.syntax = typeid(data).name(); 309 param.default_value = buf.str(); 310 param.description = description_; 311 param.action = action; 312 param.is_bool = param.info.is_type(typeid(bool)); 313 add_parameter(param_handle); 314 } 315 316 /// 317 void add_parameter(data_t any_param); 318 319 /// 320 void remove(const Parameter& param); 321 322 /// A utility function for finding a parameter given a string, 323 /// which may contain one or more characters. 324 Parameter& get_param(const char* name, bool posix); 325 326 /// Return the data_t that contains a parameter. 327 data_t get_param_any(const char* name, bool posix, bool test_if_enabled=true); 328 329 /// 330 void write_parameter_set(std::ostream& os, const std::set<data_t>& parameters, const std::string& indent) const; 331 332 /// Standardize the parameter name, by replacing '_' chars with '-' 333 std::string standardize(const std::string& name_); 334 335 /// This is where the parameter data is actually stored 336 std::set<data_t> parameter_data; 337 338 /// Category info for parameters. 339 std::map<std::string,std::set<data_t> > categories; 340 341 /// A map from a string to a parameter 342 std::map<std::string, data_t> parameters; 343 344 /// A map from a single character to a parameter 345 std::map<char, data_t> posix_parameters; 346 347 /// A list of argument/description pairs that describe the 348 /// arguments of a command 349 std::list< std::pair<std::string, std::string> > arg_definitions; 350 351 /// The list of arguments returned after parsing a command line 352 /// NOTE: the first argument is the name of the executable that was 353 /// run. 354 args_t processed_args; 355 356 /// The bool used to define the 'help' option 357 bool _help_option; 358 359 /// The bool used to define the 'version' option 360 bool _version_option; 361 362 /// 363 std::string _version_text; 364 365 /// 366 std::list<std::string> usage; 367 368 }; 369 370 } 371 372 #endif 373