Blame view

stim/parser/arguments.h 12.7 KB
2e73e7bc   David Mayerich   basic changes for...
1
2
  #ifndef STIM_ARGUMENTS
  #define STIM_ARGUMENTS
7a2d0012   David Mayerich   mirst1d updates
3
4
5
6
7
8
9
10
11
12
13
14
15
  
  #include <string>
  #include <vector>
  #include <iostream>
  #include <iomanip>
  #include <sstream>
  #include <iterator>
  #include <algorithm>
  
  #ifdef _WIN32
  #include <Windows.h>
  #endif
  
2e73e7bc   David Mayerich   basic changes for...
16
  namespace stim{
7a2d0012   David Mayerich   mirst1d updates
17
  
81e0d221   David Mayerich   separated executa...
18
  	class cmd_option
7a2d0012   David Mayerich   mirst1d updates
19
20
21
22
23
24
25
26
27
28
29
  	{
  	private:
  		bool ansi;
  
  		//argument name
  		std::string name;
  
  		//description of the argument
  		std::vector<std::string> desc;
  
  		//argument values
3b012a80   David Mayerich   added code for co...
30
31
32
33
34
35
  		std::vector<std::string> vals;
  
  		//range or example
  		std::string range;
  
  		//flag is true when the argument is user-specified
7a2d0012   David Mayerich   mirst1d updates
36
37
38
39
  		bool flag;
  
  		void parse_val(const std::string &s){
  
3b012a80   David Mayerich   added code for co...
40
41
  			vals.clear();
  
7a2d0012   David Mayerich   mirst1d updates
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  			std::stringstream ss(s);
  			std::string item;
  			while (std::getline(ss, item, ' ')) {
  				vals.push_back(item);
  			}
  		}
  
  		void parse_desc(const std::string &s){
  
  			desc.clear();
  
  			std::stringstream ss(s);
  			std::string item;
  			while (std::getline(ss, item, '\n')) {
  				desc.push_back(item);
  			}
  		}
  
3b012a80   David Mayerich   added code for co...
60
61
  	public:
  		void set_ansi(bool b){ ansi = b; }
81e0d221   David Mayerich   separated executa...
62
63
          //create an option with a given name, description, and default value
  		cmd_option(std::string _name, std::string _desc, std::string _default = "", std::string _range = "")
7a2d0012   David Mayerich   mirst1d updates
64
65
66
  		{
  			name = _name;
  			parse_desc(_desc);
3b012a80   David Mayerich   added code for co...
67
68
69
70
71
72
73
74
75
  			parse_val(_default);
  
  			//if a default value is provided, set the flag
  			if(_default != "")
                  flag = true;
              else flag = false;
  
  			range = _range;
  
7a2d0012   David Mayerich   mirst1d updates
76
  
3b012a80   David Mayerich   added code for co...
77
78
79
80
81
82
83
  		}
  
  		int nargs()
  		{
              return vals.size();
          }
  
81e0d221   David Mayerich   separated executa...
84
  		//return the value of a text option
2e73e7bc   David Mayerich   basic changes for...
85
  		std::string as_string(unsigned int n = 0)
3b012a80   David Mayerich   added code for co...
86
87
88
  		{
              if(!flag)
              {
81e0d221   David Mayerich   separated executa...
89
                  std::cout<<"ERROR - Option requested without being set: "<<name<<std::endl;
3b012a80   David Mayerich   added code for co...
90
91
92
93
94
95
96
97
98
                  exit(1);
              }
  
              if(vals.size() > n)
                  return vals[n];
  
              else return "";
  		}
  
81e0d221   David Mayerich   separated executa...
99
          //return the value of a floating point option
2e73e7bc   David Mayerich   basic changes for...
100
  		float as_float(unsigned int n = 0)
3b012a80   David Mayerich   added code for co...
101
102
103
  		{
              if(!flag)
              {
81e0d221   David Mayerich   separated executa...
104
                  std::cout<<"ERROR - option requested without being set: "<<name<<std::endl;
3b012a80   David Mayerich   added code for co...
105
106
107
108
109
110
                  exit(1);
              }
  
              if(vals.size() > n)
              {
                  float r;
2e73e7bc   David Mayerich   basic changes for...
111
                  if ( ! (std::istringstream(vals[n]) >> r) ) r = 0;
3b012a80   David Mayerich   added code for co...
112
113
114
115
116
117
                  return r;
              }
  
              else return 0;
  		}
  
81e0d221   David Mayerich   separated executa...
118
  		//return the value of an integer option
2e73e7bc   David Mayerich   basic changes for...
119
  		int as_int(unsigned int n = 0)
3b012a80   David Mayerich   added code for co...
120
121
122
  		{
              if(!flag)
              {
81e0d221   David Mayerich   separated executa...
123
                  std::cout<<"ERROR - option requested without being set: "<<name<<std::endl;
3b012a80   David Mayerich   added code for co...
124
125
126
127
128
129
                  exit(1);
              }
  
              if(vals.size() > n)
              {
                  int r;
2e73e7bc   David Mayerich   basic changes for...
130
                  if ( ! (std::istringstream(vals[n]) >> r) ) r = 0;
3b012a80   David Mayerich   added code for co...
131
132
133
134
135
136
137
138
139
140
                  return r;
              }
  
              else return 0;
  		}
  
  		//get the width of the left column
  		int col_width()
  		{
              int n = 3;
81e0d221   David Mayerich   separated executa...
141
              //add the length of the option name
3b012a80   David Mayerich   added code for co...
142
143
144
145
146
147
148
149
              n += name.size();
  
              //if there are any default parameters
              if(vals.size() > 0)
              {
                  //padding (parenthesis, =, etc.)
                  n += 6;
  
81e0d221   David Mayerich   separated executa...
150
                  //for each default option value
2e73e7bc   David Mayerich   basic changes for...
151
                  for(unsigned int v=0; v<vals.size(); v++)
3b012a80   David Mayerich   added code for co...
152
153
154
155
156
157
158
                      n += vals[v].size() + 1;
              }
  
              //add a buffer of 4 characters
              n += 4;
  
              return n;
7a2d0012   David Mayerich   mirst1d updates
159
160
161
162
163
164
  		}
  
  
  		//string output
  		std::string toStr(int width = 0)
  		{
3b012a80   David Mayerich   added code for co...
165
166
167
168
  			std::stringstream ss;
  
  			int color_size = 0;
  
7a2d0012   David Mayerich   mirst1d updates
169
170
171
172
  
  			//create the left column
  			std::string left_part = std::string(" --") + name;
  			if(vals.size() != 0)
3b012a80   David Mayerich   added code for co...
173
174
175
  			{
  				if(ansi)
  					left_part += "\033[1;32m";
7a2d0012   David Mayerich   mirst1d updates
176
  				left_part += " ( = ";
2e73e7bc   David Mayerich   basic changes for...
177
  				for(unsigned int v=0; v<vals.size(); v++)
7a2d0012   David Mayerich   mirst1d updates
178
  					left_part += vals[v] + std::string(" ");
3b012a80   David Mayerich   added code for co...
179
180
181
182
183
184
185
  				left_part += ")";
  				if(ansi)
  					left_part += "\033[0m";
  				if(ansi)
  					color_size = 11;
  			}
  			else
7a2d0012   David Mayerich   mirst1d updates
186
187
188
                  color_size = 0;
  
  			//if no width is passed, put 4 spaces between left and right columns
3b012a80   David Mayerich   added code for co...
189
  			if(width == 0) width = col_width();
7a2d0012   David Mayerich   mirst1d updates
190
191
192
193
  
  			ss<<std::left<<std::setw(width + color_size)<<left_part;
  
  			//output right column
2e73e7bc   David Mayerich   basic changes for...
194
  			for(unsigned int d=0; d<desc.size(); d++)
7a2d0012   David Mayerich   mirst1d updates
195
196
197
  			{
  				if(d == 0)
  					ss<<desc[0];
3b012a80   David Mayerich   added code for co...
198
  				else
0ef519a4   David Mayerich   optimized materia...
199
  					ss<<std::endl<<std::setfill(' ')<<std::setw(width)<<" "<<desc[d];
7a2d0012   David Mayerich   mirst1d updates
200
  
3b012a80   David Mayerich   added code for co...
201
202
203
204
  			}
  
  			//output the range in the right column
  			if(range != "" && ansi)
0ef519a4   David Mayerich   optimized materia...
205
                  ss<<std::endl<<std::setfill(' ')<<std::setw(width)<<" "<<"    "<<std::string("\033[1;36m") + range + "\033[0m";
7a2d0012   David Mayerich   mirst1d updates
206
  			else if(range != "")
0ef519a4   David Mayerich   optimized materia...
207
  				ss<<std::endl<<std::setfill(' ')<<std::setw(width)<<" "<<"    "<<range;
7a2d0012   David Mayerich   mirst1d updates
208
209
  
  			return ss.str();
3b012a80   David Mayerich   added code for co...
210
211
  		}
  
81e0d221   David Mayerich   separated executa...
212
  		//compare the name of the option to a string
3b012a80   David Mayerich   added code for co...
213
214
215
216
217
  		bool operator==(std::string rhs)
  		{
              return (name == rhs);
  		}
  
81e0d221   David Mayerich   separated executa...
218
  		//set the option to a given value
3b012a80   David Mayerich   added code for co...
219
220
221
222
223
224
225
226
227
228
229
  		void set(std::string _value)
  		{
              parse_val(_value);
  
              //set the flag
              flag = true;
  		}
  
  		bool is_set()
  		{
              return flag;
7a2d0012   David Mayerich   mirst1d updates
230
231
          }
  
3b012a80   David Mayerich   added code for co...
232
233
234
235
236
237
238
239
  	};
  
  	struct argsection
  	{
  		std::string name;
  		unsigned int index;
  	};
  
025d4bf3   David Mayerich   added documentati...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
      /**The arglist class implements command line arguments.
          Example:
      
          1) Create an arglist instance:
              
              stim::arglist args;
  
          2) Test to see if ANSI output is supported (arguments will use color if it is). Generally, Windows consoles don't support ANSI.
  
              #ifdef _WIN32
                  args.set_ansi(false);
              #endif
  
          3) Add arguments:
  
              args.add("help", "prints this help");
              args.add("foo", "foo takes a single integer value", "", "[intval]");
              args.add("bar", "bar takes two floating point values", "", "[value1], [value2]");
  
          4) You generally want to immediately test for help and output available arguments:
  
              if(args["help"].is_set())
                  std::cout<<args.str();
  
          5) Parse the command line:
  
              args.parse(argc, argv);
  
          6)  Retrieve values:
  
              int foo;
              float bar1, bar2;
              if(args["foo"])
                  foo = args["foo"].as_int();
              if(args["bar"]){
                  bar1 = args["bar"].as_float(0);
                  bar2 = args["bar"].as_float(1);
              }
  
  
      **/
  
3b012a80   David Mayerich   added code for co...
282
283
284
285
286
  	class arglist
  	{
      private:
  		bool ansi;
  
81e0d221   David Mayerich   separated executa...
287
288
289
  		//vector of options
          std::vector<cmd_option> opts;
          std::vector<std::string> args;
3b012a80   David Mayerich   added code for co...
290
  
81e0d221   David Mayerich   separated executa...
291
  		//column width of the longest option
3b012a80   David Mayerich   added code for co...
292
293
294
295
296
297
298
          int col_width;
  
  		//list of sections
  		std::vector<argsection> sections;
  
      public:
  
0ef519a4   David Mayerich   optimized materia...
299
300
301
302
          arglist(){
          	col_width = 0;
          	ansi = true;
          }
3b012a80   David Mayerich   added code for co...
303
  
025d4bf3   David Mayerich   added documentati...
304
305
306
          ///Sets ANSI support (default is on), which allows colored values in the help output.
  
          /// @param b is a boolean value specifying ANSI support (true is on, false is off)
3b012a80   David Mayerich   added code for co...
307
308
309
  		void set_ansi(bool b)
  		{
  			ansi = b;
81e0d221   David Mayerich   separated executa...
310
311
  			for(unsigned int i=0; i<opts.size(); i++)
  				opts[i].set_ansi(ansi);
3b012a80   David Mayerich   added code for co...
312
313
  		}
  
025d4bf3   David Mayerich   added documentati...
314
315
316
317
318
319
          ///Add a supported command line argument.
  
          /// @param _name is the name of the command line argument (supplied as --name)
          /// @param _desc is a text description of the argument
          /// @param _default is the default value of the argument
          /// @param _range is the supported range of values, list of values, etc. It will be displayed to the user.
3b012a80   David Mayerich   added code for co...
320
321
          void add(std::string _name, std::string _desc, std::string _default = "", std::string _range = "")
          {
81e0d221   David Mayerich   separated executa...
322
323
324
              cmd_option opt(_name, _desc, _default, _range);
  			opt.set_ansi(ansi);
              opts.push_back(opt);
3b012a80   David Mayerich   added code for co...
325
  
81e0d221   David Mayerich   separated executa...
326
              col_width = std::max<int>(col_width, opt.col_width());
3b012a80   David Mayerich   added code for co...
327
328
          }
  
025d4bf3   David Mayerich   added documentati...
329
330
331
          ///Specifies a section name that can be used to organize parameters in the output.
  
          /// @param _name is the name of the section, which will be displayed to the user
3b012a80   David Mayerich   added code for co...
332
333
334
335
  		void section(std::string _name)
  		{
  			argsection s;
  			s.name = _name;
81e0d221   David Mayerich   separated executa...
336
  			s.index = opts.size();
3b012a80   David Mayerich   added code for co...
337
338
339
  			sections.push_back(s);
  		}
  
025d4bf3   David Mayerich   added documentati...
340
          ///Outputs supported arguments. If ANSI support is available, they will be color-coded. Generally this is called in response to "--help"
7a2d0012   David Mayerich   mirst1d updates
341
          std::string str()
3b012a80   David Mayerich   added code for co...
342
343
344
345
346
347
348
349
          {
              std::stringstream ss;
  
              int si = -1;
  
              if(sections.size() > 0)
                  si = 0;
  
81e0d221   David Mayerich   separated executa...
350
351
              //for each option
              for(unsigned int a=0; a<opts.size(); a++)
3b012a80   David Mayerich   added code for co...
352
353
354
355
              {
                  if(si != -1 && a == sections[si].index)
                  {
  					if(ansi)
0ef519a4   David Mayerich   optimized materia...
356
  						ss<<std::endl<<std::left<<std::setfill('=')<<std::setw(col_width)<<std::string("\033[1;31m") + sections[si].name<<"\033[0m"<<std::endl;
3b012a80   David Mayerich   added code for co...
357
  					else
0ef519a4   David Mayerich   optimized materia...
358
  						ss<<std::endl<<std::left<<std::setfill('=')<<std::setw(col_width)<<sections[si].name<<std::endl;
3b012a80   David Mayerich   added code for co...
359
                      si++;
2e73e7bc   David Mayerich   basic changes for...
360
                      if(si == (int)sections.size()) si = -1;
3b012a80   David Mayerich   added code for co...
361
362
                  }
  
81e0d221   David Mayerich   separated executa...
363
                  ss<<opts[a].toStr(col_width)<<std::endl;
3b012a80   David Mayerich   added code for co...
364
365
366
367
368
              }
  
              return ss.str();
          }
  
025d4bf3   David Mayerich   added documentati...
369
370
371
          ///Retrieves the index for a supported argument, given that argument's name.
  
          /// @param _name is the name of the requested argument
3b012a80   David Mayerich   added code for co...
372
373
          int index(std::string _name)
          {
81e0d221   David Mayerich   separated executa...
374
          	unsigned int i = find(opts.begin(), opts.end(), _name) - opts.begin();
3b012a80   David Mayerich   added code for co...
375
  
81e0d221   David Mayerich   separated executa...
376
              if(i >= opts.size())
cac62fd3   David Mayerich   modified to work ...
377
                  return -1;
3b012a80   David Mayerich   added code for co...
378
  
cac62fd3   David Mayerich   modified to work ...
379
              return (int)i;
3b012a80   David Mayerich   added code for co...
380
381
          }
  
025d4bf3   David Mayerich   added documentati...
382
383
384
385
          ///Sets an argument to a specified value
  
          /// @param _name is the name of the argument to be set
          /// @param _value is the value that it is given
3b012a80   David Mayerich   added code for co...
386
387
388
389
390
391
          void set(std::string _name, std::string _value)
          {
              int i = index(_name);
  
              if(i != -1)
              {
81e0d221   David Mayerich   separated executa...
392
              	opts[i].set(_value);
3b012a80   David Mayerich   added code for co...
393
                  //adjust the column width if necessary
81e0d221   David Mayerich   separated executa...
394
                  col_width = (std::max)(col_width, opts[i].col_width());
3b012a80   David Mayerich   added code for co...
395
396
              }
              else
81e0d221   David Mayerich   separated executa...
397
                  std::cout<<"ERROR - option not recognized: "<<_name<<std::endl;
3b012a80   David Mayerich   added code for co...
398
399
          }
  
025d4bf3   David Mayerich   added documentati...
400
401
402
403
          ///Parses the command line
  
          /// @param argc is the number of command line arguments (provided by the OS)
          /// @param argv[] is the list of command line arguments (provided by the OS)
3b012a80   David Mayerich   added code for co...
404
405
          void parse(int argc, char* argv[])
          {
81e0d221   David Mayerich   separated executa...
406
              //if the number of options is 1, we're done
3b012a80   David Mayerich   added code for co...
407
408
409
410
411
              if(argc <= 1) return;
  
              std::string name;
              std::string params;
  
81e0d221   David Mayerich   separated executa...
412
413
              bool args_done = false;		//create a flag that turns true when the first option is encountered
  
3b012a80   David Mayerich   added code for co...
414
415
              for(int i=1; i<argc; i++)
              {
81e0d221   David Mayerich   separated executa...
416
                  //if the argument is an option
3b012a80   David Mayerich   added code for co...
417
418
                  if(argv[i][0] == '-' && argv[i][1] == '-')
                  {
81e0d221   David Mayerich   separated executa...
419
420
                  	args_done = true;				//arguments for the executable are done, all options now
                      //add any previous options
3b012a80   David Mayerich   added code for co...
421
422
                      if(name != "")
                          set(name, params);
81e0d221   David Mayerich   separated executa...
423
                      //set the current option to this name
3b012a80   David Mayerich   added code for co...
424
425
426
427
                      name = argv[i]+2;
                      //clear the parameters list
                      params = "";
                  }
81e0d221   David Mayerich   separated executa...
428
429
430
431
                  else if(!args_done){
                  	args.push_back(argv[i]);
                  }
                  else{	//everything else is an arg for the most recent option
3b012a80   David Mayerich   added code for co...
432
433
434
435
436
437
  					if(params != "")
  						params += " ";
                      params += argv[i];
                  }
              }
  
81e0d221   David Mayerich   separated executa...
438
              //set the last option
3b012a80   David Mayerich   added code for co...
439
440
441
              set(name, params);
          }
  
025d4bf3   David Mayerich   added documentati...
442
443
444
          ///Determines of a parameter has been set and returns true if it has
  
          /// @param _name is the name of the argument
3b012a80   David Mayerich   added code for co...
445
446
          bool operator()(std::string _name)
          {
81e0d221   David Mayerich   separated executa...
447
              int i = find(opts.begin(), opts.end(), _name) - opts.begin();
3b012a80   David Mayerich   added code for co...
448
449
450
451
452
453
454
  
              if(i < 0)
              {
                  std::cout<<"ERROR - Unspecified parameter name: "<<_name<<std::endl;
                  exit(1);
              }
  
81e0d221   David Mayerich   separated executa...
455
              return opts[i].is_set();
3b012a80   David Mayerich   added code for co...
456
457
          }
  
025d4bf3   David Mayerich   added documentati...
458
459
460
          ///Returns the number of parameters for a specified argument
  
          /// @param _name is the name of the argument whose parameter number will be returned
81e0d221   David Mayerich   separated executa...
461
          unsigned int nargs(std::string _name)
3b012a80   David Mayerich   added code for co...
462
          {
81e0d221   David Mayerich   separated executa...
463
              int i = find(opts.begin(), opts.end(), _name) - opts.begin();
3b012a80   David Mayerich   added code for co...
464
465
466
467
468
469
470
  
              if(i < 0)
              {
                  std::cout<<"ERROR - Unspecified parameter name: "<<_name<<std::endl;
                  exit(1);
              }
  
81e0d221   David Mayerich   separated executa...
471
472
473
              return opts[i].nargs();
          }
  
025d4bf3   David Mayerich   added documentati...
474
          ///Returns the number of arguments that have been set
81e0d221   David Mayerich   separated executa...
475
476
477
478
          unsigned int nargs(){
          	return args.size();
          }
  
025d4bf3   David Mayerich   added documentati...
479
480
481
          ///Returns the name of an argument, given its index
  
          /// @param a is the index of the requested argument
81e0d221   David Mayerich   separated executa...
482
483
          std::string arg(unsigned int a){
          	return args[a];
3b012a80   David Mayerich   added code for co...
484
485
          }
  
025d4bf3   David Mayerich   added documentati...
486
487
488
          ///Returns an object describing the argument
  
          /// @param _name is the name of the requested argument
81e0d221   David Mayerich   separated executa...
489
          cmd_option operator[](std::string _name)
3b012a80   David Mayerich   added code for co...
490
          {
81e0d221   David Mayerich   separated executa...
491
              int i = find(opts.begin(), opts.end(), _name) - opts.begin();
3b012a80   David Mayerich   added code for co...
492
  
695d32fa   unknown   David's changes t...
493
              if(i < 0 || i >= opts.size())
3b012a80   David Mayerich   added code for co...
494
495
496
497
498
              {
                  std::cout<<"ERROR - Unspecified parameter name: "<<_name<<std::endl;
                  exit(1);
              }
  
81e0d221   David Mayerich   separated executa...
499
              return opts[i];
3b012a80   David Mayerich   added code for co...
500
501
502
          }
  
  
7a2d0012   David Mayerich   mirst1d updates
503
504
505
506
  	};
  
  
  
2e73e7bc   David Mayerich   basic changes for...
507
  }	//end namespace stim
3b012a80   David Mayerich   added code for co...
508
509
  
  #endif