polaroid-pp

Schlieren and contour plot tool
git clone https://git.0xfab.ch/polaroid-pp.git
Log | Files | Refs | Submodules | README | LICENSE

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 };