CMS 3D CMS Logo

CmdLine.h
Go to the documentation of this file.
1 #ifndef CONDTOOLS_HCAL_CMDLINE_H_
2 #define CONDTOOLS_HCAL_CMDLINE_H_
3 
4 //=========================================================================
5 // CmdLine.h
6 //
7 // Simple command line parser for the C++ "main" program. It provides
8 // functionality of "getopt" and "getopt_long" with a convenient interface.
9 // Typical usage is as follows:
10 //
11 // #include <necessary standard headers>
12 //
13 // #include "CmdLine.h"
14 //
15 // using namespace cmdline;
16 //
17 // int main(int argc, char *argv[])
18 // {
19 // CmdLine cmdline(argc, argv);
20 //
21 // int i = 0;
22 // double d = 0;
23 // bool b = false;
24 // std::string requiredOption;
25 // std::vector<std::string> positionalArgs;
26 //
27 // try {
28 // /* Arguments are short and long versions of the option name. */
29 // /* Long version can be omitted. If you want to use the long */
30 // /* version only, call the two-argument method with the short */
31 // /* version set to 0. */
32 // cmdline.option("-i", "--integer") >> i;
33 // cmdline.option("-d") >> d;
34 //
35 // /* Options that must be present on the command line */
36 // cmdline.require("-r", "--required") >> requiredOption;
37 //
38 // /* Switches that do not require subsequent arguments */
39 // b = cmdline.has("-b");
40 //
41 // /* Declare the end of option processing. Unconsumed options */
42 // /* will cause an exception to be thrown. */
43 // cmdline.optend();
44 //
45 // /* Process all remaining arguments */
46 // while (cmdline)
47 // {
48 // std::string s;
49 // cmdline >> s;
50 // positionalArgs.push_back(s);
51 // }
52 // }
53 // catch (const CmdLineError& e) {
54 // std::cerr << "Error in " << cmdline.progname() << ": "
55 // << e.str() << std::endl;
56 // return 1;
57 // }
58 //
59 // /* ..... Do main processing here ..... */
60 //
61 // return 0;
62 // }
63 //
64 // Short version options must use a single character. It is possible
65 // to combine several short options together on the command line,
66 // for example, "-xzvf" is equivalent to "-x -z -v -f".
67 //
68 // Use standalone "-" to indicate that the next argument is either
69 // an option value or the program argument (but not a switch), even
70 // if it starts with "-". This is useful if the option value has to
71 // be set to a negative number.
72 //
73 // Use standalone "--" (not preceded by standalone "-") to indicate
74 // the end of options. All remaining arguments will be treated as
75 // program arguments, even if they start with "-".
76 //
77 // Note that each of the parsing methods of the CmdLine class ("option",
78 // "has", and "require") is greedy. These methods will consume all
79 // corresponding options or switches and will set the result to the last
80 // option value seen. It is therefore impossible to provide a collection
81 // of values by using an option more than once. This is done in order to
82 // avoid difficulties with deciding what to do when multiple option values
83 // were consumed by the user code only partially.
84 //
85 // After the "optend()" call, the "argc()" method of the CmdLine
86 // class can be used to determine the number of remaining program
87 // arguments. If the expected number of arguments is known in advance,
88 // the simplest way to get the arguments out is like this (should be
89 // inside the "try" block):
90 //
91 // if (cmdline.argc() != n_expected)
92 // throw CmdLineError("wrong number of command line arguments");
93 // cmdline >> arg0 >> arg1 >> ...;
94 //
95 // I. Volobouev
96 // January 2016
97 //=========================================================================
98 
99 #include <list>
100 #include <sstream>
101 #include <cstring>
102 #include <utility>
103 #include <cstdio>
104 
105 #ifdef __GNUC__
106 #include <cstdlib>
107 #include <typeinfo>
108 #include <cxxabi.h>
109 #endif
110 
111 #ifdef __GXX_EXPERIMENTAL_CXX0X__
112 #include <memory>
113 #define CmdLine_shared_ptr std::shared_ptr
114 #else
115 #include <tr1/memory>
116 #define CmdLine_shared_ptr std::tr1::shared_ptr
117 #endif
118 
119 namespace cmdline {
120 
121  // Subsequent classes will throw exceptions of the following class
122  class CmdLineError {
123  public:
124  inline CmdLineError(const char* msg = nullptr) : os_(new std::ostringstream()) {
125  if (msg)
126  *os_ << msg;
127  }
128 
129  template <typename T>
130  inline CmdLineError& operator<<(const T& obj) {
131  *os_ << obj;
132  return *this;
133  }
134 
135  inline std::string str() const { return os_->str(); }
136 
137  private:
138  CmdLine_shared_ptr<std::ostringstream> os_;
139  };
140 
141  template <typename T>
142  inline void OneShotExtract(std::istringstream& is, T& obj) {
143  is >> obj;
144  }
145 
146  template <>
147  inline void OneShotExtract<std::string>(std::istringstream& is, std::string& obj) {
148  obj = is.str();
149  is.seekg(0, std::ios_base::end);
150  }
151 
153  public:
155 
157 
158  inline operator void*() const { return valid_ && !readout_ ? (void*)this : (void*)nullptr; }
159 
160  template <typename T>
161  inline bool operator>>(T& obj) {
162  if (readout_)
163  throw CmdLineError() << "can't reuse command line argument \"" << str_ << '"';
164  readout_ = true;
165  if (valid_) {
166  std::istringstream is(str_);
167  OneShotExtract(is, obj);
168  if (is.bad() || is.fail())
169  throw CmdLineError() << "failed to parse command line argument \"" << str_ << '"'
170 #ifdef __GNUC__
171  << ", " << demangle(obj) << " expected"
172 #endif
173  ;
174  if (is.peek() != EOF)
175  throw CmdLineError() << "extra characters in command line argument \"" << str_ << '"'
176 #ifdef __GNUC__
177  << ", " << demangle(obj) << " expected"
178 #endif
179  ;
180  }
181  return valid_;
182  }
183 
184  inline bool isValid() const { return valid_; }
185 
186  private:
188  bool valid_;
189  bool readout_;
190 
191 #ifdef __GNUC__
192  template <typename T>
193  inline std::string demangle(T& obj) const {
194  int status;
195  const std::type_info& ti = typeid(obj);
196  char* realname = abi::__cxa_demangle(ti.name(), nullptr, nullptr, &status);
197  std::string s(realname);
198  free(realname);
199  return s;
200  }
201 #endif
202  };
203 
204  class CmdLine {
205  // Argument codes (second member of the pair):
206  // 0 -- possible option value (or program argument, not yet known which)
207  // 1 -- short option switch
208  // 2 -- long option switch
209  // 3 -- program argument
210  typedef std::pair<std::string, int> Pair;
211  typedef std::list<Pair> Optlist;
212 
213  inline Optlist::iterator find(const char* shortOpt, const char* longOpt) {
214  Optlist::iterator iend = args_.end();
215  for (Optlist::iterator it = args_.begin(); it != iend; ++it) {
216  if (shortOpt && it->second == 1 && it->first == shortOpt)
217  return it;
218  if (longOpt && it->second == 2 && it->first == longOpt)
219  return it;
220  }
221  return iend;
222  }
223 
224  public:
225  inline CmdLine(const unsigned argc, const char* const argv[]) : nprogargs_(0) {
226  // Parse the program name
227  const char* progname = std::strrchr(argv[0], '/');
228  if (progname)
229  ++progname;
230  else
231  progname = argv[0];
232 
233  // Take into account program name mangling by GNU autotools
234  if (strncmp(progname, "lt-", 3) == 0)
235  progname += 3;
237 
238  // Make a list of arguments noting on the way if this is
239  // a short option, long option, or possible option argument
240  bool previousIsOpt = false;
241  bool nextIsArg = false;
242  for (unsigned i = 1; i < argc; ++i) {
243  if (nextIsArg) {
244  args_.push_back(Pair(argv[i], previousIsOpt ? 0 : 3));
245  previousIsOpt = false;
246  ++nprogargs_;
247  nextIsArg = false;
248  } else if (strcmp(argv[i], "-") == 0)
249  nextIsArg = true;
250  else if (strcmp(argv[i], "--") == 0) {
251  // End of options
252  for (unsigned k = i + 1; k < argc; ++k) {
253  args_.push_back(Pair(argv[k], 3));
254  ++nprogargs_;
255  }
256  return;
257  } else if (strncmp(argv[i], "--", 2) == 0) {
258  args_.push_back(Pair(argv[i], 2));
259  previousIsOpt = true;
260  } else if (argv[i][0] == '-') {
261  const unsigned len = strlen(argv[i]);
262  for (unsigned k = 1; k < len; ++k) {
263  std::string dummy("-");
264  dummy += argv[i][k];
265  args_.push_back(Pair(dummy, 1));
266  previousIsOpt = true;
267  }
268  } else {
269  args_.push_back(Pair(argv[i], previousIsOpt ? 0 : 3));
270  previousIsOpt = false;
271  ++nprogargs_;
272  }
273  }
274  }
275 
276  inline const char* progname() const { return progname_.c_str(); }
277 
278  inline bool has(const char* shortOpt, const char* longOpt = nullptr) {
279  bool found = false;
280  for (Optlist::iterator it = find(shortOpt, longOpt); it != args_.end(); it = find(shortOpt, longOpt)) {
281  found = true;
282  Optlist::iterator it0(it);
283  if (++it != args_.end())
284  if (it->second == 0)
285  it->second = 3;
286  args_.erase(it0);
287  }
288  return found;
289  }
290 
291  inline OneShotIStream option(const char* shortOpt, const char* longOpt = nullptr) {
293  for (Optlist::iterator it = find(shortOpt, longOpt); it != args_.end(); it = find(shortOpt, longOpt)) {
294  Optlist::iterator it0(it);
295  if (++it != args_.end())
296  if (it->second == 0) {
297  result = OneShotIStream(it->first);
298  args_.erase(it0, ++it);
299  --nprogargs_;
300  continue;
301  }
302  throw CmdLineError() << "missing command line argument for option \"" << it0->first << '"';
303  }
304  return result;
305  }
306 
307  inline OneShotIStream require(const char* shortOpt, const char* longOpt = nullptr) {
308  const OneShotIStream& is(option(shortOpt, longOpt));
309  if (!is.isValid()) {
310  const char empty[] = "";
311  const char* s = shortOpt ? shortOpt : (longOpt ? longOpt : empty);
312  throw CmdLineError() << "required command line option \"" << s << "\" is missing";
313  }
314  return is;
315  }
316 
317  inline void optend() const {
318  for (Optlist::const_iterator it = args_.begin(); it != args_.end(); ++it)
319  if (it->second == 1 || it->second == 2)
320  throw CmdLineError("invalid command line option \"") << it->first << '"';
321  }
322 
323  inline operator void*() const { return (void*)(static_cast<unsigned long>(nprogargs_)); }
324 
325  inline unsigned argc() const { return nprogargs_; }
326 
327  template <typename T>
328  inline CmdLine& operator>>(T& obj) {
329  if (!nprogargs_)
330  throw CmdLineError("no more input available on the command line");
331  Optlist::iterator it = args_.begin();
332  for (; it != args_.end(); ++it)
333  if (it->second == 0 || it->second == 3)
334  break;
335  OneShotIStream is(it->first);
336  args_.erase(it);
337  --nprogargs_;
338  is >> obj;
339  return *this;
340  }
341 
342  private:
343  CmdLine() = delete;
344 
347  unsigned nprogargs_;
348  };
349 
350 } // namespace cmdline
351 
352 #endif // CONDTOOLS_HCAL_CMDLINE_H_
OneShotIStream option(const char *shortOpt, const char *longOpt=nullptr)
Definition: CmdLine.h:291
std::list< Pair > Optlist
Definition: CmdLine.h:211
std::string str() const
Definition: CmdLine.h:135
std::pair< std::string, int > Pair
Definition: CmdLine.h:210
CmdLineError(const char *msg=nullptr)
Definition: CmdLine.h:124
void optend() const
Definition: CmdLine.h:317
bool isValid() const
Definition: CmdLine.h:184
OneShotIStream(const std::string &s)
Definition: CmdLine.h:156
std::tr1::shared_ptr< std::ostringstream > os_
Definition: CmdLine.h:138
std::string str_
Definition: CmdLine.h:187
bool has(const char *shortOpt, const char *longOpt=nullptr)
Definition: CmdLine.h:278
void OneShotExtract(std::istringstream &is, T &obj)
Definition: CmdLine.h:142
unsigned argc() const
Definition: CmdLine.h:325
const char * progname() const
Definition: CmdLine.h:276
CmdLine & operator>>(T &obj)
Definition: CmdLine.h:328
unsigned nprogargs_
Definition: CmdLine.h:347
CmdLineError & operator<<(const T &obj)
Definition: CmdLine.h:130
OneShotIStream require(const char *shortOpt, const char *longOpt=nullptr)
Definition: CmdLine.h:307
CmdLine(const unsigned argc, const char *const argv[])
Definition: CmdLine.h:225
tuple msg
Definition: mps_check.py:286
std::string progname_
Definition: CmdLine.h:345
long double T
bool operator>>(T &obj)
Definition: CmdLine.h:161
Optlist::iterator find(const char *shortOpt, const char *longOpt)
Definition: CmdLine.h:213
Optlist args_
Definition: CmdLine.h:346