The spica renderer
argparse.h
1 #ifdef _MSC_VER
2 #pragma once
3 #endif
4 
5 #ifndef ARGPARSE_H
6 #define ARGPARSE_H
7 
8 #include <iostream>
9 #include <sstream>
10 #include <string>
11 #include <vector>
12 #include <algorithm>
13 #include <unordered_map>
14 
15 namespace spica {
16 
18 private:
19  struct Arg {
20  Arg(char shortName, const std::string &longName, const std::string &value, bool required)
21  : shortName{ shortName }
22  , longName{ longName }
23  , value{ value }
24  , required { required } {
25  }
26 
27  char shortName;
28  std::string longName;
29  std::string value;
30  bool required;
31  };
32 
33 public:
34  ArgumentParser() = default;
35 
36  virtual ~ArgumentParser() {
37  }
38 
39  template <typename T>
40  void addArgument(const std::string &shortName, const std::string &longName, T value, bool required = false) {
41  std::string v = std::to_string(value);
42  addArgument<std::string>(shortName, longName, v, required);
43  }
44 
45  bool parse(int argc, char **argv) {
46  appName = std::string(argv[0]);
47  for (int i = 1; i < argc; i++) {
48  if (!isValidShort(argv[i]) && !isValidLong(argv[i])) {
49  fprintf(stderr, "[WARNING] Unparsed argument: \"%s\"\n", argv[i]);
50  continue;
51  }
52 
53  std::string longName = "";
54  if (isValidShort(argv[i])) {
55  const auto &it = short2long.find(parseShort(argv[i]));
56  if (it == short2long.cend()) {
57  fprintf(stderr, "[WARNING] Unknown flag %s!\n", argv[i]);
58  continue;
59  }
60  longName = it->second;
61  } else {
62  longName = parseLong(argv[i]);
63  }
64 
65  if (table.find(longName) != table.cend()) {
66  values[longName] = std::string(argv[i + 1]);
67  i += 1;
68  }
69  }
70 
71  // Check requirements
72  bool success = true;
73  for (const auto &arg : args) {
74  if (arg.required) {
75  const auto it = values.find(arg.longName);
76  if (it == values.cend()) {
77  success = false;
78  fprintf(stderr, "Required argument \"%s\" is not specified!\n", arg.longName.c_str());
79  }
80  }
81  }
82 
83  return success;
84  }
85 
86  std::string helpText() {
87  std::stringstream ss;
88  ss << appName << " ";
89 
90  // First, print required arguments.
91  for (const auto &arg : args) {
92  if (arg.required) {
93  ss << ("--" + arg.longName) << " " << toUpper(arg.longName) << " ";
94  }
95  }
96 
97  // Then, print non-required arguments.
98  for (const auto &arg : args) {
99  if (!arg.required) {
100  ss << "[" << ("--" + arg.longName) << " " << toUpper(arg.longName) << "] ";
101  }
102  }
103 
104  return ss.str();
105  }
106 
107  int getInt(const std::string &name) const {
108  const auto &it = values.find(name);
109  if (it == values.cend()) {
110  fprintf(stderr, "Unknown name: %s!\n", name.c_str());
111  return 0;
112  }
113  return std::atoi(it->second.c_str());
114  }
115 
116  double getDouble(const std::string &name) const {
117  const auto &it = values.find(name);
118  if (it == values.cend()) {
119  fprintf(stderr, "Unknown name: %s!\n", name.c_str());
120  return 0.0;
121  }
122  return std::atof(it->second.c_str());
123  }
124 
125  bool getBool(const std::string &name) const {
126  const auto &it = values.find(name);
127  if (it == values.cend()) {
128  fprintf(stderr, "Unknown name: %s!\n", name.c_str());
129  return false;
130  }
131 
132  std::string value = it->second;
133  if (value == "True" || value == "true" || value == "Yes" || value == "yes") {
134  return true;
135  }
136 
137  if (value == "False" || value == "false" || value == "No" || value == "no") {
138  return false;
139  }
140 
141  throw std::runtime_error("Cannot parse spacified option to bool!");
142  }
143 
144  std::string getString(const std::string &name) const {
145  const auto &it = values.find(name);
146  if (it == values.cend()) {
147  fprintf(stderr, "Unknown name: %s!\n", name.c_str());
148  return "";
149  }
150  return it->second;
151  }
152 
153 private:
154  // Private methods
155  static bool isValidShort(const std::string &name) {
156  return name.length() == 2 && name[0] == '-';
157  }
158 
159  static bool isValidLong(const std::string &name) {
160  return name.length() > 2 && name[0] == '-' && name[1] == '-';
161  }
162 
163  static char parseShort(const std::string &name) {
164  if (!isValidShort(name)) {
165  throw std::runtime_error("Short name must be a single charactor with a hyphen!");
166  }
167  return name[1];
168  }
169 
170  static std::string parseLong(const std::string &name) {
171  if (!isValidLong(name)) {
172  throw std::runtime_error("Long name must begin with two hyphens!");
173  }
174  return name.substr(2);
175  }
176 
177  static std::string toUpper(const std::string &value) {
178  std::string ret = value;
179  std::transform(ret.begin(), ret.end(), ret.begin(), toupper);
180  return ret;
181  }
182 
183  // Private parameters
184  std::string appName;
185  std::vector<Arg> args;
186  std::unordered_map<std::string, uint32_t> table;
187  std::unordered_map<char, std::string> short2long;
188  std::unordered_map<std::string, std::string> values;
189 };
190 
191 // ---------------------------------------------------------------------------------------------------------------------
192 // Template specialization
193 // ---------------------------------------------------------------------------------------------------------------------
194 
195 template <>
196 void ArgumentParser::addArgument<std::string>(const std::string &shortName, const std::string &longName,
197  std::string value, bool required) {
198  const char sname = parseShort(shortName);
199  const std::string lname = parseLong(longName);
200  short2long.insert(std::make_pair(sname, lname));
201  table.insert(std::make_pair(lname, args.size()));
202  args.emplace_back(sname, lname, value, required);
203  if (!required) {
204  values.insert(std::make_pair(lname, value));
205  }
206 }
207 
208 template <>
209 void ArgumentParser::addArgument<const char*>(const std::string &shortName, const std::string &longName,
210  const char *value, bool required) {
211  addArgument<std::string>(shortName, longName, std::string(value), required);
212 }
213 
214 } // namespace spica
215 
216 #endif // _ARG_PARSER_H_
Definition: argparse.h:17