Arty
options.h
1 #ifndef OPTIONS_H
2 #define OPTIONS_H
3 
4 #include <iostream>
5 #include <string>
6 #include <vector>
7 #include <cassert>
8 #include <cstdlib>
9 #include <algorithm>
10 #include <cstring>
11 
12 template <typename T>
13 struct OptionReader {
14  static bool read(const char* arg, T& value) {
15  return false;
16  }
17 };
18 
19 template <>
20 struct OptionReader<bool> {
21  static bool read(const char* arg, bool& value) {
22  if (!std::strcmp(arg, "true") || (arg[0] == '1' && arg[1] == '\0') || arg[0] == '\0') {
23  value = true;
24  } else if (!std::strcmp(arg, "false") || (arg[0] == '0' && arg[1] == '\0')) {
25  value = false;
26  } else {
27  value = false;
28  return false;
29  }
30  return true;
31  }
32 };
33 
34 template <>
35 struct OptionReader<std::string> {
36  static bool read(const char* arg, std::string& value) {
37  value = arg;
38  return true;
39  }
40 };
41 
42 template <>
43 struct OptionReader<int> {
44  static bool read(const char* arg, int& value) {
45  char* tmp;
46  value = std::strtol(arg, &tmp, 10);
47  return tmp != arg;
48  }
49 };
50 
51 template <>
52 struct OptionReader<size_t> {
53  static bool read(const char* arg, size_t& value) {
54  char* tmp;
55  value = std::strtoul(arg, &tmp, 10);
56  return tmp != arg;
57  }
58 };
59 
60 template <>
61 struct OptionReader<float> {
62  static bool read(const char* arg, float& value) {
63  char* tmp;
64  value = std::strtof(arg, &tmp);
65  return tmp != arg;
66  }
67 };
68 
69 template <>
70 struct OptionReader<double> {
71  static bool read(const char* arg, double& value) {
72  char* tmp;
73  value = std::strtod(arg, &tmp);
74  return tmp != arg;
75  }
76 };
77 
78 template <typename T>
79 struct OptionWriter {
80  static std::string write(const T& value) {
81  return std::to_string(value);
82  }
83 };
84 
85 template <>
86 struct OptionWriter<std::string> {
87  static std::string write(const std::string& value) {
88  return value;
89  }
90 };
91 
92 struct Option {
93  Option(const std::string& fn,
94  const std::string& sn,
95  const std::string& dc)
96  : full_name(fn)
97  , short_name(sn)
98  , desc(dc)
99  {}
100 
101  virtual ~Option() {}
102 
103  virtual std::ostream& print_default(std::ostream& o) const { return o; }
104  virtual bool read_value(const char* arg) = 0;
105  virtual std::string arg_name() const { return ""; }
106  virtual bool has_arg() const { return false; }
107 
108  std::string full_name;
109  std::string short_name;
110  std::string desc;
111 };
112 
113 template <typename T>
114 struct OptionImpl : public Option {
115  OptionImpl(const std::string& full_name,
116  const std::string& short_name,
117  const std::string& desc,
118  T& val, const T& def,
119  const std::string& arg)
120  : Option(full_name, short_name, desc)
121  , value(val)
122  , default_value(def)
123  , arg_desc(arg)
124  {
125  value = default_value;
126  }
127 
128  std::ostream& print_default(std::ostream& o) const override { return o << default_value; }
129 
130  bool read_value(const char* arg) override {
131  return OptionReader<T>::read(arg, value);
132  }
133 
134  bool has_arg() const override { return true; }
135  std::string arg_name() const override { return arg_desc; }
136 
137  T& value;
138  T default_value;
139  std::string arg_desc;
140 };
141 
142 template <>
143 struct OptionImpl<bool> : public Option {
144  OptionImpl(const std::string& full_name,
145  const std::string& short_name,
146  const std::string& desc,
147  bool& val, const bool& def,
148  const std::string&)
149  : Option(full_name, short_name, desc)
150  , value(val)
151  , default_value(def)
152  {
153  value = default_value;
154  }
155 
156  bool read_value(const char* arg) override {
157  return OptionReader<bool>::read(arg, value);
158  }
159 
160  bool& value;
161  bool default_value;
162 };
163 
165 class ArgParser {
166 public:
167  ArgParser(int argc, char** argv)
168  : argc(argc), argv(argv)
169  {
170  std::string path(argv[0]);
171  exe_name = path.substr(path.find_last_of("\\/") + 1);
172  }
173 
174  ~ArgParser() {
175  for (auto opt : options) {
176  delete opt;
177  }
178  }
179 
180  template <typename T>
181  void add_option(const std::string& full_name,
182  const std::string& short_name,
183  const std::string& desc,
184  T& value,
185  const T& default_value = T(),
186  const std::string& arg_name = std::string()) {
187  options.push_back(new OptionImpl<T>(full_name, short_name, desc, value, default_value, arg_name));
188  }
189 
190  bool parse() {
191  for (int i = 1; i < argc; i++) {
192  if (argv[i][0] == '-') {
193  // This is an option
194  if (argv[i][1] == '-') {
195  // Try full name
196  const std::string full_opt(argv[i] + 2);
197  auto eq_pos = full_opt.find('=');
198  const std::string opt_name = full_opt.substr(0, eq_pos);
199  const std::string opt_arg = (eq_pos != std::string::npos) ? full_opt.substr(eq_pos + 1) : std::string();
200 
201  auto it = std::find_if(options.begin(), options.end(), [this, opt_name] (const Option* opt) -> bool {
202  return opt->full_name == opt_name;
203  });
204 
205  if (it == options.end()) {
206  std::cerr << "Unknown option : " << opt_name << std::endl;
207  return false;
208  }
209 
210  if ((*it)->has_arg()) {
211  if (eq_pos == std::string::npos) {
212  std::cerr << "Missing argument for option : " << opt_name << std::endl;
213  return false;
214  }
215 
216  if (!(*it)->read_value(opt_arg.c_str())) {
217  std::cerr << "Invalid value given to option \'" << opt_name << "\' : " << opt_arg << std::endl;
218  return false;
219  }
220  } else {
221  if (eq_pos != std::string::npos) {
222  std::cerr << "Option \'" << opt_name << "\' does not accept any argument" << std::endl;
223  return false;
224  }
225 
226  bool ok = (*it)->read_value(opt_arg.c_str());
227  UNUSED(ok);
228  assert(ok && "read_value returns false for an option without args");
229  }
230  } else {
231  // Try short name
232  auto it = std::find_if(options.begin(), options.end(), [this, i] (const Option* opt) -> bool {
233  return opt->short_name.compare(argv[i] + 1) == 0;
234  });
235 
236  if (it == options.end()) {
237  std::cerr << "Unknown option : " << argv[i] + 1 << std::endl;
238  return false;
239  }
240 
241  if ((*it)->has_arg()) {
242  if (i >= argc - 1) {
243  std::cerr << "Missing argument for option : " << argv[i] + 1 << std::endl;
244  return false;
245  }
246 
247  if(!(*it)->read_value(argv[i + 1])) {
248  std::cerr << "Invalid value given to option \'" << argv[i] + 1 << "\' : " << argv[i + 1] << std::endl;
249  return false;
250  }
251 
252  i++;
253  } else {
254  if(!(*it)->read_value("")) {
255  std::cerr << "Invalid value given to option \'" << argv[i] + 1 << std::endl;
256  return false;
257  }
258  }
259  }
260  } else {
261  // This is an argument
262  args.push_back(argv[i]);
263  }
264  }
265  return true;
266  }
267 
268  void usage() const {
269  std::cout << "Usage : " << exe_name << " [options] files\n"
270  << "Available options :\n";
271 
272  // Compute amount of space needed to align text
273  size_t short_offset = 0, full_offset = 0;
274  for (const auto opt : options) {
275  auto a = opt->has_arg() ? opt->arg_name().length() + 2 : 0;
276  auto s = opt->short_name.length() + a;
277  auto f = opt->full_name.length() + a;
278 
279  if (short_offset < s) short_offset = s;
280  if (full_offset < f) full_offset = f;
281  }
282 
283  for (const auto opt : options) {
284  auto s = opt->short_name.length();
285  auto f = opt->full_name.length();
286 
287  if (opt->has_arg()) {
288  auto a = opt->arg_name().length();
289  std::cout << "\t-" << opt->short_name << " " << opt->arg_name() << std::string(short_offset - s - a, ' ')
290  << "--" << opt->full_name << "=" << opt->arg_name() << std::string(full_offset - f - a, ' ')
291  << opt->desc << " (defaults to \'";
292  opt->print_default(std::cout) << "\').\n";
293  } else {
294  std::cout << "\t-" << opt->short_name << std::string(short_offset - s + 1, ' ')
295  << "--" << opt->full_name << std::string(full_offset - f + 1, ' ')
296  << opt->desc << ".\n";
297  }
298  }
299 
300  std::cout << std::endl;
301  }
302 
303  const std::vector<std::string>& arguments() const {
304  return args;
305  }
306 
307 private:
308  std::vector<Option*> options;
309  std::vector<std::string> args;
310  std::string exe_name;
311  int argc;
312  char** argv;
313 };
314 
315 #endif // OPTIONS_H
Command line argument parser with ability to display the program usage.
Definition: options.h:165
STL namespace.
Definition: options.h:114
Definition: options.h:79
Definition: options.h:92
Definition: options.h:13