ArgumentParser.h (14596B)
1 /* 2 * ArgumentParser.h 3 * Cubism 4 * 5 * This argument parser assumes that all arguments are optional ie, each of the argument names is preceded by a '-' 6 * all arguments are however NOT optional to avoid a mess with default values and returned values when not found! 7 * 8 * More converter could be required: 9 * add as needed 10 * TypeName as{TypeName}() in Value 11 * 12 * Created by Christian Conti on 6/7/10. 13 * Copyright 2010 ETH Zurich. All rights reserved. 14 * 15 */ 16 17 #pragma once 18 #include <cstdio> 19 #include <cstdlib> 20 #include <cstring> 21 #include <ctime> 22 #include <map> 23 #include <vector> 24 #include <string> 25 #include <iostream> 26 #include <sstream> 27 #include <iomanip> 28 #include <fstream> 29 #include <ostream> 30 #include <limits> 31 32 33 using namespace std; 34 35 class Value 36 { 37 private: 38 string content; 39 40 public: 41 42 Value() : content("") {} 43 44 Value(string content_) : content(content_) { /*printf("%s\n",content.c_str());*/ } 45 46 Value(const Value& c) : content(c.content) {} 47 48 Value& operator=(const Value& rhs) 49 { 50 if (this != &rhs) 51 content = rhs.content; 52 return *this; 53 } 54 Value& operator+=(const Value& rhs) 55 { 56 content += " " + rhs.content; 57 return *this; 58 } 59 Value operator+(const Value& rhs) { return Value(content + " " + rhs.content); } 60 61 double asDouble(double def=0) 62 { 63 if (content == "") 64 { 65 ostringstream sbuf; 66 sbuf << def; 67 content = sbuf.str(); 68 } 69 return (double) atof(content.c_str()); 70 } 71 72 int asInt(int def=0) 73 { 74 if (content == "") 75 { 76 ostringstream sbuf; 77 sbuf << def; 78 content = sbuf.str(); 79 } 80 return atoi(content.c_str()); 81 } 82 83 bool asBool(bool def=false) 84 { 85 if (content == "") 86 { 87 if (def) content = "true"; 88 else content = "false"; 89 } 90 if (content == "0") return false; 91 if (content == "false") return false; 92 93 return true; 94 } 95 96 string asString(string def="") 97 { 98 if (content == "") content = def; 99 100 return content; 101 } 102 103 friend std::ostream& operator<<(std::ostream& lhs, const Value& rhs) 104 { 105 lhs << rhs.content; 106 return lhs; 107 } 108 }; 109 110 111 class CommandlineParser 112 { 113 private: 114 const int iArgC; 115 const char** vArgV; 116 bool bStrictMode, bVerbose; 117 118 protected: 119 map<string,Value> mapArguments; 120 121 inline void _normalizeKey(std::string& key) const 122 { 123 if (key[0] == '-') key.erase(0,1); 124 if (key[0] == '+') key.erase(0,1); 125 } 126 127 inline bool _existKey(const std::string& key, const std::map<std::string,Value>& container) const 128 { 129 std::map<std::string,Value>::const_iterator it = container.find(key); 130 return it != container.end(); 131 } 132 133 public: 134 135 Value& operator()(string key) 136 { 137 _normalizeKey(key); 138 if (bStrictMode) 139 { 140 if (!_existKey(key,mapArguments)) 141 { 142 printf("Runtime option NOT SPECIFIED! ABORTING! name: %s\n",key.data()); 143 abort(); 144 } 145 } 146 147 if (bVerbose) printf("%s is %s\n", key.data(), mapArguments[key].asString().data()); 148 return mapArguments[key]; 149 } 150 151 inline bool check(string key) const 152 { 153 _normalizeKey(key); 154 return _existKey(key,mapArguments); 155 } 156 157 CommandlineParser(const int argc, const char ** argv) : mapArguments(), iArgC(argc), vArgV(argv), bStrictMode(false), bVerbose(true) 158 { 159 for (int i=1; i<argc; i++) 160 if (argv[i][0] == '-') 161 { 162 string values = ""; 163 int itemCount = 0; 164 165 for (int j=i+1; j<argc; j++) 166 { 167 const bool leadingDash = (argv[j][0] == '-'); 168 const char c = argv[j][1]; 169 const bool firstNumeric = ((c >= '0' && c <= '9') || c == 0) ? true : false; 170 if (leadingDash && !firstNumeric) 171 break; 172 else 173 { 174 if (strcmp(values.c_str(), "")) 175 values += ' '; 176 177 values += argv[j]; 178 itemCount++; 179 } 180 } 181 182 if (itemCount == 0) 183 values = "true"; 184 185 std::string key(argv[i]); 186 key.erase(0,1); // remove leading '-' 187 if (key[0] == '+') 188 { 189 key.erase(0,1); 190 if (!_existKey(key,mapArguments)) 191 mapArguments[key] = Value(values); // skip leading white space 192 else 193 mapArguments[key] += Value(values); 194 } 195 else 196 { 197 if (!_existKey(key,mapArguments)) 198 mapArguments[key] = Value(values); 199 } 200 201 i += itemCount; 202 } 203 204 mute(); 205 //printf("found %ld arguments of %d\n",mapArguments.size(),argc); 206 } 207 208 int getargc() const { return iArgC; } 209 210 const char** getargv() const { return vArgV; } 211 212 void set_strict_mode() 213 { 214 bStrictMode = true; 215 } 216 217 void unset_strict_mode() 218 { 219 bStrictMode = false; 220 } 221 222 void mute() 223 { 224 bVerbose = false; 225 } 226 227 void loud() 228 { 229 bVerbose = true; 230 } 231 232 void save_options(string path=".") 233 { 234 string options; 235 for(map<string,Value>::iterator it=mapArguments.begin(); it!=mapArguments.end(); it++) 236 { 237 options+= it->first + " " + it->second.asString() + " "; 238 } 239 string filepath = (path + "/" + string("argumentparser.log")); 240 FILE * f = fopen(filepath.data(), "a"); 241 if (f == NULL) 242 { 243 printf("impossible to write %s.\n", filepath.data()); 244 return; 245 } 246 fprintf(f, "%s\n", options.data()); 247 fclose(f); 248 } 249 250 void print_args() 251 { 252 for(map<string,Value>::iterator it=mapArguments.begin(); it!=mapArguments.end(); it++) 253 { 254 std::cout.width(50); 255 std::cout.fill('.'); 256 std::cout << std::left << it->first; 257 std::cout << ": " << it->second.asString() << std::endl; 258 } 259 } 260 }; 261 262 263 class ArgumentParser: public CommandlineParser 264 { 265 typedef std::map<std::string, Value> ArgMap; 266 typedef std::map<std::string, Value*> pArgMap; 267 typedef std::map<std::string, ArgMap* > FileMap; 268 269 const char commentStart; 270 271 // keep a reference from option origin 272 ArgMap from_commandline; 273 FileMap from_files; 274 pArgMap from_code; 275 276 // for runtime interaction (we keep the original map) 277 ArgMap mapRuntime; 278 279 // helper 280 void _ignoreComments(std::istream& stream, const char commentChar) 281 { 282 stream >> std::ws; 283 int nextchar = stream.peek(); 284 while (nextchar == commentChar) 285 { 286 stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 287 stream >> std::ws; 288 nextchar = stream.peek(); 289 } 290 } 291 292 void _parseFile(std::ifstream& stream, ArgMap& container) 293 { 294 // read (key value) pairs from input file, ignore comments 295 // beginning with commentStart 296 _ignoreComments(stream, commentStart); 297 while (!stream.eof()) 298 { 299 std::string line, key, val; 300 std::getline(stream, line); 301 std::istringstream lineStream(line); 302 lineStream >> key; 303 lineStream >> val; 304 _ignoreComments(lineStream, commentStart); 305 while(!lineStream.eof()) 306 { 307 std::string multiVal; 308 lineStream >> multiVal; 309 val += (" " + multiVal); 310 _ignoreComments(lineStream, commentStart); 311 } 312 313 const Value V(val); 314 if (key[0] == '-') 315 key.erase(0,1); 316 317 if (key[0] == '+') 318 { 319 key.erase(0,1); 320 if (!_existKey(key,container)) // skip leading white space 321 container[key] = V; 322 else 323 container[key] += V; 324 } 325 else if (!_existKey(key,container)) 326 container[key] = V; 327 _ignoreComments(stream, commentStart); 328 } 329 } 330 331 332 public: 333 ArgumentParser(const int _argc, const char ** _argv, const char cstart='#'): 334 CommandlineParser(_argc, _argv), commentStart(cstart) 335 { 336 from_commandline = mapArguments; 337 } 338 339 virtual ~ArgumentParser() 340 { 341 for (FileMap::iterator it = from_files.begin(); it != from_files.end(); it++) 342 delete it->second; 343 } 344 345 void readFile(const std::string filepath) 346 { 347 from_files[filepath] = new ArgMap; 348 ArgMap& myFMap = *(from_files[filepath]); 349 350 std::ifstream confFile(filepath.c_str()); 351 if (confFile.good()) 352 { 353 _parseFile(confFile, mapArguments); 354 confFile.clear(); 355 confFile.seekg(0, ios::beg); 356 _parseFile(confFile, myFMap); // we keep a reference for each separate file read 357 } 358 confFile.close(); 359 } 360 361 Value& operator()(std::string key) 362 { 363 _normalizeKey(key); 364 const bool bDefaultInCode = !_existKey(key,mapArguments); 365 Value& retval = CommandlineParser::operator()(key); 366 if (bDefaultInCode) from_code[key] = &retval; 367 return retval; 368 } 369 370 inline bool exist(std::string key) const { return check(key); } 371 372 void write_runtime_environment() const 373 { 374 time_t rawtime; 375 std::time(&rawtime); 376 struct tm* timeinfo = std::localtime(&rawtime); 377 char buf[256]; 378 std::strftime(buf, 256, "%A, %h %d %Y, %r", timeinfo); 379 380 std::ofstream runtime("runtime_environment.conf"); 381 runtime << commentStart << " RUNTIME ENVIRONMENT SETTINGS" << std::endl; 382 runtime << commentStart << " ============================" << std::endl; 383 runtime << commentStart << " " << buf << std::endl; 384 runtime << commentStart << " Use this file to set runtime parameter interactively." << std::endl; 385 runtime << commentStart << " The parameter are read every \"refreshperiod\" steps." << std::endl; 386 runtime << commentStart << " When editing this file, you may use comments and string concatenation." << std::endl; 387 runtime << commentStart << " The simulation can be terminated without killing it by setting \"exit\" to true." << std::endl; 388 runtime << commentStart << " (This will write a serialized restart state. Set \"exitsave\" to false if not desired.)" << std::endl; 389 runtime << commentStart << std::endl; 390 runtime << commentStart << " !!! WARNING !!! EDITING THIS FILE CAN POTENTIALLY CRASH YOUR SIMULATION !!! WARNING !!!" << std::endl; 391 for (typename std::map<std::string,Value>::const_iterator it = mapArguments.begin(); it != mapArguments.end(); ++it) 392 runtime << it->first << '\t' << it->second << std::endl; 393 } 394 395 void read_runtime_environment() 396 { 397 mapRuntime.clear(); 398 std::ifstream runtime("runtime_environment.conf"); 399 if (runtime.good()) 400 _parseFile(runtime, mapRuntime); 401 runtime.close(); 402 } 403 404 Value& parseRuntime(std::string key) 405 { 406 _normalizeKey(key); 407 if (!_existKey(key,mapRuntime)) 408 { 409 printf("ERROR: Runtime parsing for key %s NOT FOUND!! Check your runtime_environment.conf file\n",key.data()); 410 abort(); 411 } 412 return mapRuntime[key]; 413 } 414 415 void print_args() 416 { 417 std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl; 418 std::cout << "* Summary:" << std::endl; 419 std::cout << "* Parameter read from command line: " << from_commandline.size() << std::endl; 420 size_t nFiles = 0; 421 size_t nFileParameter = 0; 422 for (FileMap::const_iterator it=from_files.begin(); it!=from_files.end(); ++it) 423 { 424 if (it->second->size() > 0) 425 { 426 ++nFiles; 427 nFileParameter += it->second->size(); 428 } 429 } 430 std::cout << "* Parameter read from " << std::setw(3) << std::right << nFiles << " file(s): " << nFileParameter << std::endl; 431 std::cout << "* Parameter read from defaults in code: " << from_code.size() << std::endl; 432 std::cout << "* Total number of parameter read from all sources: " << mapArguments.size() << std::endl; 433 std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl; 434 435 // command line given arguments 436 if (!from_commandline.empty()) 437 { 438 std::cout << "* Command Line:" << std::endl; 439 std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl; 440 for(ArgMap::iterator it=from_commandline.begin(); it!=from_commandline.end(); it++) 441 { 442 std::cout.width(50); 443 std::cout.fill('.'); 444 std::cout << std::left << it->first; 445 std::cout << ": " << it->second.asString() << std::endl; 446 } 447 std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl; 448 } 449 450 // options read from input files 451 if (!from_files.empty()) 452 { 453 for (FileMap::iterator itFile=from_files.begin(); itFile!=from_files.end(); itFile++) 454 { 455 if (!itFile->second->empty()) 456 { 457 std::cout << "* File: " << itFile->first << std::endl; 458 std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl; 459 ArgMap& fileArgs = *(itFile->second); 460 for(ArgMap::iterator it=fileArgs.begin(); it!=fileArgs.end(); it++) 461 { 462 std::cout.width(50); 463 std::cout.fill('.'); 464 std::cout << std::left << it->first; 465 std::cout << ": " << it->second.asString() << std::endl; 466 } 467 std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl; 468 } 469 } 470 } 471 472 // defaults defined in code 473 if (!from_code.empty()) 474 { 475 std::cout << "* Defaults in Code:" << std::endl; 476 std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl; 477 for(pArgMap::iterator it=from_code.begin(); it!=from_code.end(); it++) 478 { 479 std::cout.width(50); 480 std::cout.fill('.'); 481 std::cout << std::left << it->first; 482 std::cout << ": " << it->second->asString() << std::endl; 483 } 484 std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << std::endl; 485 } 486 } 487 };