GRUTinizer
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ArgParser.h
Go to the documentation of this file.
1 #ifndef _ARGPARSER_H_
2 #define _ARGPARSER_H_
3 
4 #include <sstream>
5 #include <stdexcept>
6 #include <string>
7 #include <vector>
8 
9 struct ParseError : public std::runtime_error{
10  ParseError(const char* msg) : std::runtime_error(msg) { }
11  ParseError(const std::string& msg) : std::runtime_error(msg) { }
12 };
13 
14 // struct ParseError : public std::runtime_error{
15 // using std::runtime_error::runtime_error;
16 // };
17 
19 public:
20  ArgParseItem() : present_(false) { }
21  virtual ~ArgParseItem(){ }
22  virtual bool matches(const std::string& flag) const = 0;
23  virtual void parse_item(const std::vector<std::string>& arguments) = 0;
24  virtual int num_arguments() const = 0;
25  virtual std::string printable(int description_column = -1, int* chars_before_desc=NULL) const = 0;
26  virtual bool is_required() const = 0;
27  bool is_present() const {return present_;}
28  virtual std::string flag_name() const = 0;
29 
30  void parse(const std::string& name, const std::vector<std::string>& arguments){
31  if((num_arguments()==-1 && arguments.size()==0) ||
32  (num_arguments()!=-1 && arguments.size() != size_t(num_arguments()))){
33  std::stringstream ss;
34  if(num_arguments()==-1){
35  ss << "Flag \"" << name << "\" expected at least one argument";
36  } else {
37  ss << "Flag \"" << name << "\" expected " << num_arguments()
38  << " argument(s) and received " << arguments.size();
39  }
40  throw ParseError(ss.str());
41  }
42 
43  present_ = true;
44  parse_item(arguments);
45  }
46 
47 private:
48  bool present_;
49 };
50 
51 template<typename T>
52 class ArgParseConfig : public ArgParseItem {
53 public:
54  ArgParseConfig(std::string flag_list)
55  : desc(""), required_(false) {
56  std::stringstream ss(flag_list);
57  while(!ss.eof()){
58  std::string temp;
59  ss >> temp;
60  if(temp.length() == 1){
61  flags.push_back("-" + temp);
62  } else if (temp.length() > 1) {
63  flags.push_back("--" + temp);
64  }
65  }
66  }
67  virtual ~ArgParseConfig(){ }
68 
69  virtual std::string flag_name() const {
70  std::string output;
71  for(auto flag : flags){
72  if(flag.length() > output.length()){
73  output = flag;
74  }
75  }
76  return output;
77  }
78 
79  virtual bool matches(const std::string& flag) const {
80  // This is the default option, and something not a flag was passed.
81  if(flag.at(0)!='-' && flags.size()==0){
82  return true;
83  }
84 
85  for(auto& f : flags){
86  if(f == flag){
87  return true;
88  }
89  }
90  return false;
91  }
92 
93  virtual ArgParseConfig& description(const std::string& d){
94  desc = d;
95  return *this;
96  }
97 
99  required_ = true;
100  return *this;
101  }
102 
103  virtual bool is_required() const {
104  return required_;
105  }
106 
107  virtual ArgParseConfig& default_value(T value) = 0;
108 
109  virtual std::string printable(int description_column = -1, int* chars_before_desc=NULL) const {
110  std::stringstream ss;
111 
112  ss << " ";
113 
114  bool has_singlechar_flag = false;
115  for(auto flag : flags){
116  if(flag.length()==2){
117  ss << flag << " ";
118  has_singlechar_flag = true;
119  }
120  }
121  for(auto flag : flags){
122  if(flag.length()!=2){
123  if(has_singlechar_flag){
124  ss << "[ ";
125  }
126  ss << flag;
127  if(has_singlechar_flag){
128  ss << " ]";
129  }
130  }
131  }
132 
133  if(num_arguments()!=0){
134  ss << " arg ";
135  }
136 
137  auto chars = ss.tellp();
138  if(chars_before_desc){
139  *chars_before_desc = chars;
140  }
141 
142  if(description_column != -1 &&
143  chars < description_column){
144  for(unsigned int i=0; i<description_column-chars; i++){
145  ss << " ";
146  }
147  }
148 
149  ss << desc;
150 
151  return ss.str();
152  }
153 
154 protected:
155  std::string desc;
156  std::vector<std::string> flags;
157  bool required_;
158 };
159 
160 template<typename T>
161 class ArgParseConfigT : public ArgParseConfig<T> {
162 public:
163  ArgParseConfigT(std::string flag, T* output_location)
164  : ArgParseConfig<T>(flag), output_location(output_location) { }
165 
168  return *this;
169  }
170 
171  virtual void parse_item(const std::vector<std::string>& arguments){
172  std::stringstream ss(arguments[0]);
173  ss >> *output_location;
174  }
175 
176  virtual int num_arguments() const { return 1; }
177 
178 
179 private:
181 };
182 
183 template<>
184 class ArgParseConfigT<bool> : public ArgParseConfig<bool> {
185 public:
186  ArgParseConfigT(std::string flag, bool* output_location)
187  : ArgParseConfig<bool>(flag), output_location(output_location),
188  stored_default_value(false) {
189  *output_location = stored_default_value;
190  }
191 
194  stored_default_value = value;
195  return *this;
196  }
197 
198  virtual void parse_item(const std::vector<std::string>& /*arguments*/){
199  *output_location = !stored_default_value;
200  }
201 
202  virtual int num_arguments() const { return 0; }
203 
204 private:
207 };
208 
209 template<typename T>
210 class ArgParseConfigT<std::vector<T> > : public ArgParseConfig<std::vector<T> > {
211 public:
212  ArgParseConfigT(std::string flag, std::vector<T>* output_location)
213  : ArgParseConfig<std::vector<T> >(flag), output_location(output_location),
214  num_arguments_expected(-1) { }
215 
216  virtual void parse_item(const std::vector<std::string>& arguments){
217  for(auto arg : arguments){
218  std::stringstream ss(arg);
219  T val;
220  ss >> val;
221  output_location->push_back(val);
222  }
223  }
224 
225  virtual int num_arguments() const { return num_arguments_expected; }
226 
229  return *this;
230  }
231 
232 private:
233  std::vector<T>* output_location;
235 };
236 
237 class ArgParser {
238 public:
239  ArgParser() { }
240 
242  for(auto val : values){
243  delete val;
244  }
245  }
246 
247  void parse(int argc, char** argv) {
248  bool double_dash_encountered = false;
249 
250  int iarg = 1;
251  while(iarg < argc){
252 
253  std::string arg = argv[iarg++];
254 
255  if(arg.at(0) != '-' ||
256  double_dash_encountered){
257  handle_default_option(argc, argv, iarg);
258  } else if(arg.substr(0,2) == "--"){
259  handle_long_flag(argc, argv, iarg);
260  } else {
261  handle_short_flag(argc, argv, iarg);
262  }
263  }
264 
265  for(auto val : values){
266  if(val->is_required() && !val->is_present()){
267  std::stringstream ss;
268  ss << "Required argument \"" << val->flag_name() << "\" is not present";
269  throw ParseError(ss.str());
270  }
271  }
272  }
273 
274  template<typename T>
275  ArgParseConfigT<T>& option(const std::string flag, T* output_location){
276  ArgParseConfigT<T>* output = new ArgParseConfigT<T>(flag, output_location);
277  values.push_back(output);
278  return *output;
279  }
280 
281  template<typename T>
282  ArgParseConfigT<std::vector<T> >& default_option(std::vector<T>* output_location){
283  return option("", output_location);
284  }
285 
286  void print(std::ostream& out) const {
287  out << "Options:\n";
288 
289  int max_length = -1;
290  for(auto item : values){
291  int length;
292  item->printable(-1, &length);
293  max_length = std::max(length, max_length);
294  }
295 
296  for(auto it = values.begin(); it!=values.end(); it++){
297  ArgParseItem* item = *it;
298  out << item->printable(max_length);
299  if(it!=values.end()-1){
300  out << "\n";
301  }
302  }
303  }
304 
305 private:
306  void handle_long_flag(int argc, char** argv, int& iarg){
307  std::string arg = argv[iarg-1];
308  std::vector<std::string> flag_args;
309  std::string flag;
310  size_t equals_index = arg.find("=");
311  if(equals_index == std::string::npos){
312  // flag followed by list of flag_args
313  flag = arg;
314  } else {
315  // = inside flag
316  flag = arg.substr(0, equals_index);
317  }
318 
319  ArgParseItem& item = get_item(flag);
320 
321  if(equals_index == std::string::npos){
322  flag_args = argument_list(argc, argv, iarg, item.num_arguments());
323  } else {
324  flag_args.push_back(arg.substr(equals_index+1));
325  }
326 
327  item.parse(flag, flag_args);
328  }
329 
330  void handle_short_flag(int argc, char** argv, int& iarg){
331  std::string arg = argv[iarg-1];
332  std::string flag = arg.substr(0,2);
333  ArgParseItem& item = get_item(flag);
334  if(item.num_arguments() == 0){
335  // Each character is a boolean flag
336  for(unsigned int ichar=1; ichar<arg.length(); ichar++){
337  std::string flag = "-" + arg.substr(ichar,1);
338  std::vector<std::string> flag_args;
339  get_item(flag).parse(flag, flag_args);
340  }
341  } else {
342  if(arg.length() == 2){
343  // Next arguments passed to the program get passed to the flag.
344  std::vector<std::string> flag_args = argument_list(argc, argv, iarg, item.num_arguments());
345  item.parse(flag, flag_args);
346  } else {
347  // Everything past the first character is argument to the flag.
348  std::vector<std::string> flag_args{arg.substr(2)};
349  item.parse(flag, flag_args);
350  }
351  }
352  }
353 
354  void handle_default_option(int /*argc*/, char** argv, int& iarg){
355  std::string arg = argv[iarg-1];
356  std::vector<std::string> flag_args{arg};
357 
358  ArgParseItem& item = get_item(arg);
359  item.parse(arg, flag_args);
360  }
361 
363  std::vector<std::string> argument_list(int argc, char** argv, int& iarg, int max_args){
364  std::vector<std::string> output;
365  bool read_extra = false;
366  while(iarg<argc &&
367  (max_args==-1 || output.size() < size_t(max_args))){
368  std::string next_arg = argv[iarg++];
369  if(next_arg.at(0) == '-'){
370  read_extra = true;
371  break;
372  } else {
373  output.push_back(next_arg);
374  }
375  }
376  if(read_extra){
377  iarg--;
378  }
379  return output;
380  }
381 
382  ArgParseItem& get_item(const std::string& flag){
383  for(auto val : values){
384  if(val->matches(flag)){
385  return *val;
386  }
387  }
388 
389  std::stringstream ss;
390  if(flag.at(0)=='-'){
391  ss << "Unknown option: \"" << flag << "\"";
392  } else {
393  ss << "Was passed \"" << flag << "\" as a non-option argument, when no non-option arguments are allowed";
394  }
395  throw ParseError(ss.str());
396  }
397 
398  std::vector<ArgParseItem*> values;
399 };
400 
401 std::ostream& operator<<(std::ostream& out, const ArgParser& val){
402  val.print(out);
403  return out;
404 }
405 
406 #endif /* _ARGPARSER_H_ */