Static Value-Flow Analysis
Loading...
Searching...
No Matches
CommandLine.h
Go to the documentation of this file.
1#ifndef COMMANDLINE_H_
2#define COMMANDLINE_H_
3
4#include <algorithm>
5#include <cassert>
6#include <string>
7#include <tuple>
8#include <type_traits>
9#include <unordered_map>
10#include <unordered_set>
11#include <limits>
12#include <map>
13#include <vector>
14#include <cstdlib>
15#include <iostream>
16#include <sstream>
17
18typedef unsigned u32_t;
19
21{
22protected:
24 typedef std::pair<std::string, std::string> PossibilityDescription;
25 typedef std::vector<std::pair<std::string, std::string>> PossibilityDescriptions;
26
29 template <typename T>
30 using OptionPossibility = std::tuple<T, std::string, std::string>;
31
32protected:
33 OptionBase(std::string name, std::string description)
35 {
36 }
37
40 {
41 assert(name[0] != '-' && "OptionBase: name starts with '-'");
42 assert(!isHelpName(name) && "OptionBase: reserved help name");
43 assert(getOptionsMap().find(name) == getOptionsMap().end() && "OptionBase: duplicate option");
44
45 // Types with empty names (i.e., OptionMultiple) can handle things themselves.
46 if (!name.empty())
47 {
48 // Standard name=value option.
49 getOptionsMap()[name] = this;
50 }
51 }
52
54 virtual bool parseAndSetValue(const std::string value) = 0;
55
58 virtual bool isBool(void) const
59 {
60 return false;
61 }
62
64 virtual bool isMultiple(void) const
65 {
66 return false;
67 }
68
70 virtual bool canSet(void) const = 0;
71
72public:
75 static std::vector<std::string> parseOptions(int argc, char *argv[], std::string description, std::string callFormat)
76 {
77 const std::string usage = buildUsage(description, std::string(argv[0]), callFormat);
78
79 std::vector<std::string> positionalArguments;
80
81 for (int i = 1; i < argc; ++i)
82 {
83 std::string arg(argv[i]);
84 if (arg.empty()) continue;
85 if (arg[0] != '-')
86 {
87 // Positional argument. NOT a value to another argument because we
88 // "skip" over evaluating those without the corresponding argument.
89 positionalArguments.push_back(arg);
90 continue;
91 }
92
93 // Chop off '-'.
94 arg = arg.substr(1);
95
96 std::string argName;
97 std::string argValue;
98 OptionBase *opt = nullptr;
99
100 size_t equalsSign = arg.find('=');
101 if (equalsSign != std::string::npos)
102 {
103 // Argument has an equal sign, i.e. argName=argValue
104 argName = arg.substr(0, equalsSign);
105 if (isHelpName(argName)) usageAndExit(usage, false);
106
107 opt = getOption(argName);
108 if (opt == nullptr)
109 {
110 std::cerr << "Unknown option: " << argName << std::endl;
111 usageAndExit(usage, true);
112 }
113
114 argValue = arg.substr(equalsSign + 1);
115 }
116 else
117 {
118 argName = arg;
119 if (isHelpName(argName)) usageAndExit(usage, false);
120
121 opt = getOption(argName);
122 if (opt == nullptr)
123 {
124 std::cerr << "Unknown option: " << argName << std::endl;
125 usageAndExit(usage, true);
126 }
127
128 // No equals sign means we may need next argument.
129 if (opt->isBool())
130 {
131 // Booleans do not accept -arg true/-arg false.
132 // They must be -arg=true/-arg=false.
133 argValue = "true";
134 }
135 else if (opt->isMultiple())
136 {
137 // Name is the value and will be converted to an enum.
138 argValue = argName;
139 }
140 else if (i + 1 < argc)
141 {
142 // On iteration, we'll skip the value.
143 ++i;
144 argValue = std::string(argv[i]);
145 }
146 else
147 {
148 std::cerr << "Expected value for: " << argName << std::endl;
149 usageAndExit(usage, true);
150 }
151 }
152
153 if (!opt->canSet())
154 {
155 std::cerr << "Unable to set: " << argName << "; check for duplicates" << std::endl;
156 usageAndExit(usage, true);
157 }
158
159 bool valueSet = opt->parseAndSetValue(argValue);
160 if (!valueSet)
161 {
162 std::cerr << "Bad value for: " << argName << std::endl;
163 usageAndExit(usage, true);
164 }
165 }
166
167 return positionalArguments;
168 }
169
170private:
174 static std::string buildUsage(
175 const std::string description, const std::string argv0, const std::string callFormat
176 )
177 {
178 // Determine longest option to split into two columns: options and descriptions.
179 unsigned longest = 0;
180 for (const std::pair<std::string, OptionBase *> nopt : getOptionsMap())
181 {
182 const std::string name = std::get<0>(nopt);
183 const OptionBase *option = std::get<1>(nopt);
184 if (option->isMultiple())
185 {
186 // For Multiple, description goes in left column.
187 if (option->description.length() > longest) longest = option->description.length();
188 }
189 else
190 {
191 if (name.length() > longest) longest = name.length();
192 }
193
194 for (const PossibilityDescription &pd : option->possibilityDescriptions)
195 {
196 const std::string possibility = std::get<0>(pd);
197 if (possibility.length() + 3 > longest) longest = possibility.length() + 3;
198 }
199 }
200
201 std::stringstream ss;
202
203 ss << description << std::endl << std::endl;
204
205 ss << "USAGE:" << std::endl;
206 ss << " " << argv0 << " " << callFormat << std::endl;
207 ss << std::endl;
208
209 ss << "OPTIONS:" << std::endl;
210
211 // Required as we have OptionMultiples doing a many-to-one in options.
212 std::unordered_set<const OptionBase *> handled;
213 for (const std::pair<std::string, OptionBase *> nopt : getOptionsMap())
214 {
215 const std::string name = std::get<0>(nopt);
216 const OptionBase *option = std::get<1>(nopt);
217 if (handled.find(option) != handled.end()) continue;
218 handled.insert(option);
219
220 if (option->isMultiple())
221 {
222 // description
223 // -name1 - description
224 // -name2 - description
225 // ...
226 ss << " " << option->description << std::endl;
227 for (const PossibilityDescription &pd : option->possibilityDescriptions)
228 {
229 const std::string possibility = std::get<0>(pd);
230 const std::string description = std::get<1>(pd);
231 ss << " -" << possibility << std::string(longest - possibility.length() + 2, ' ');
232 ss << "- " << description << std::endl;
233 }
234 }
235 else
236 {
237 // name - description
238 // or
239 // name - description
240 // =opt1 - description
241 // =opt2 - description
242 // ...
243 ss << " -" << name << std::string(longest - name.length() + 2, ' ');
244 ss << "- " << option->description << std::endl;
245 for (const PossibilityDescription &pd : option->possibilityDescriptions)
246 {
247 const std::string possibility = std::get<0>(pd);
248 const std::string description = std::get<1>(pd);
249 ss << " =" << possibility << std::string(longest - possibility.length() + 2, ' ');
250 ss << "- " << description << std::endl;
251 }
252 }
253 }
254
255 // Help message.
256 ss << std::endl;
257 ss << " -help" << std::string(longest - 4 + 2, ' ') << "- show usage and exit" << std::endl;
258 ss << " -h" << std::string(longest - 1 + 2, ' ') << "- show usage and exit" << std::endl;
259
260 // How to set boolean options.
261 ss << std::endl;
262 ss << "Note: for boolean options, -name true and -name false are invalid." << std::endl;
263 ss << " Use -name, -name=true, or -name=false." << std::endl;
264
265 return ss.str();
266 }
267
269 static OptionBase *getOption(const std::string optName)
270 {
271 auto optIt = getOptionsMap().find(optName);
272 if (optIt == getOptionsMap().end()) return nullptr;
273 else return optIt->second;
274 }
275
277 static void usageAndExit(const std::string usage, bool error)
278 {
279 if (error) std::cerr << usage;
280 else std::cout << usage;
281 std::exit(error ? 1 : 0);
282 }
283
285 static bool isHelpName(const std::string name)
286 {
287 static std::vector<std::string> helpNames = {"help", "h", "-help"};
288 return std::find(helpNames.begin(), helpNames.end(), name) != helpNames.end();
289 }
290
291protected:
292 // Return the name/description part of OptionsPossibilities (second and third fields).
293 template<typename T>
295 {
297 for (const OptionPossibility<T> &op : possibilities)
298 {
299 possibilityDescriptions.push_back(std::make_pair(std::get<1>(op), std::get<2>(op)));
300 }
301
303 }
304
307 static std::map<std::string, OptionBase *> &getOptionsMap(void)
308 {
309 // Not static member to avoid initialisation order problems.
310 static std::map<std::string, OptionBase *> options;
311 return options;
312 }
313
314
315protected:
316 std::string name;
317 std::string description;
320};
321
324template <typename T>
325class Option : public OptionBase
326{
327public:
328 Option(const std::string& name, const std::string& description, T init)
330 {
331 assert(!name.empty() && "Option: empty option name given");
332 }
333
334 virtual bool canSet(void) const override
335 {
336 // Don't allow duplicates.
337 return !isExplicitlySet;
338 }
339
340 virtual bool parseAndSetValue(const std::string s) override
341 {
343 return isExplicitlySet;
344 }
345
346 virtual bool isBool(void) const override
347 {
348 return std::is_same<T, bool>::value;
349 }
350
351 void setValue(T v)
352 {
353 value = v;
354 }
355
356 T operator()(void) const
357 {
358 return value;
359 }
360
361private:
362 // Convert string to boolean, returning whether we succeeded.
363 static bool fromString(const std::string& s, bool& value)
364 {
365 if (s == "true")
366 value = true;
367 else if (s == "false")
368 value = false;
369 else
370 return false;
371 return true;
372 }
373
374 // Convert string to string, always succeeds.
375 static bool fromString(const std::string s, std::string &value)
376 {
377 value = s;
378 return true;
379 }
380
381 // Convert string to u32_t, returning whether we succeeded.
382 static bool fromString(const std::string s, u32_t &value)
383 {
384 // We won't allow anything except [0-9]+.
385 if (s.empty()) return false;
386 for (char c : s)
387 {
388 if (!(c >= '0' && c <= '9')) return false;
389 }
390
391 // Use strtoul because we're not using exceptions.
392 assert(sizeof(unsigned long) >= sizeof(u32_t));
393 const unsigned long sv = std::strtoul(s.c_str(), nullptr, 10);
394
395 // Out of range according to strtoul, or according to us compared to u32_t.
396 if (errno == ERANGE || sv > std::numeric_limits<u32_t>::max()) return false;
397 value = sv;
398 return true;
399 }
400
401private:
404};
405
408template <typename T>
409class OptionMap : public OptionBase
410{
411public:
412 typedef std::vector<OptionPossibility<T>> OptionPossibilities;
413
414 OptionMap(std::string name, std::string description, T init, OptionPossibilities possibilities)
417 {
418 assert(!name.empty() && "OptionMap: empty option name given");
419 }
420
421 virtual bool canSet(void) const override
422 {
423 return !isExplicitlySet;
424 }
425
426 virtual bool parseAndSetValue(const std::string s) override
427 {
428 for (const OptionPossibility<T> &op : possibilities)
429 {
430 // Check if the given string is a valid one.
431 if (s == std::get<1>(op))
432 {
433 // What that string maps to.
434 value = std::get<0>(op);
435 isExplicitlySet = true;
436 break;
437 }
438 }
439
440 return isExplicitlySet;
441 }
442
443 T operator()(void) const
444 {
445 return value;
446 }
447
448private:
452};
453
456template <typename T>
458{
459public:
460 typedef std::vector<OptionPossibility<T>> OptionPossibilities;
461
464 {
465 for (const OptionPossibility<T> &op : possibilities)
466 {
467 optionValues[std::get<0>(op)] = false;
468 }
469
470 for (const OptionPossibility<T> &op : possibilities)
471 {
472 std::string possibilityName = std::get<1>(op);
473 getOptionsMap()[possibilityName] = this;
474 }
475 }
476
477 virtual bool canSet(void) const override
478 {
479 return true;
480 }
481
482 virtual bool parseAndSetValue(const std::string s) override
483 {
484 // Like in OptionMap basically, except we can have many values.
485 for (const OptionPossibility<T> &op : possibilities)
486 {
487 if (s == std::get<1>(op))
488 {
489 optionValues[std::get<0>(op)] = true;
490 return true;
491 }
492 }
493
494 return false;
495 }
496
497 virtual bool isMultiple(void) const override
498 {
499 return true;
500 }
501
503 bool nothingSet(void) const
504 {
505 for (const std::pair<T, bool> tb : optionValues)
506 {
507 if (tb.second) return false;
508 }
509
510 return true;
511 }
512
514 bool operator()(const T v) const
515 {
516 typename std::unordered_map<T, bool>::const_iterator ovIt = optionValues.find(v);
517 // TODO: disabled as we check for "invalid" values in some places.
518 // assert(ovIt != optionValues.end() && "OptionMultiple: bad value checked");
519 if (ovIt == optionValues.end()) return false;
520 return ovIt->second;
521 }
522
523private:
526 std::unordered_map<T, bool> optionValues;
528};
529
530#endif /* COMMANDLINE_H_ */
unsigned u32_t
Definition CommandLine.h:18
#define false
Definition cJSON.cpp:70
std::pair< std::string, std::string > PossibilityDescription
Name/description pairs.
Definition CommandLine.h:24
static PossibilityDescriptions extractPossibilityDescriptions(const std::vector< OptionPossibility< T > > possibilities)
OptionBase(std::string name, std::string description, PossibilityDescriptions possibilityDescriptions)
Definition CommandLine.h:38
virtual bool canSet(void) const =0
Can this option be set?
std::string name
static OptionBase * getOption(const std::string optName)
Find option based on name in options map. Returns nullptr if not found.
static std::string buildUsage(const std::string description, const std::string argv0, const std::string callFormat)
std::tuple< T, std::string, std::string > OptionPossibility
Definition CommandLine.h:30
std::string description
static std::vector< std::string > parseOptions(int argc, char *argv[], std::string description, std::string callFormat)
Definition CommandLine.h:75
PossibilityDescriptions possibilityDescriptions
For when we have possibilities like in an OptionMap.
static void usageAndExit(const std::string usage, bool error)
Print usage and exit. If error is set, print to stderr and exits with code 1.
virtual bool isBool(void) const
Definition CommandLine.h:58
virtual bool isMultiple(void) const
Whether this option is an OptionMultiple.
Definition CommandLine.h:64
std::vector< std::pair< std::string, std::string > > PossibilityDescriptions
Definition CommandLine.h:25
static bool isHelpName(const std::string name)
Returns whether name is one of the reserved help options.
OptionBase(std::string name, std::string description)
Definition CommandLine.h:33
static std::map< std::string, OptionBase * > & getOptionsMap(void)
virtual bool parseAndSetValue(const std::string value)=0
From a given string, set the value of this option.
OptionPossibilities possibilities
T operator()(void) const
std::vector< OptionPossibility< T > > OptionPossibilities
virtual bool parseAndSetValue(const std::string s) override
From a given string, set the value of this option.
bool isExplicitlySet
virtual bool canSet(void) const override
Can this option be set?
OptionMap(std::string name, std::string description, T init, OptionPossibilities possibilities)
OptionMultiple(std::string description, OptionPossibilities possibilities)
std::unordered_map< T, bool > optionValues
bool nothingSet(void) const
If the bitset is empty.
std::vector< OptionPossibility< T > > OptionPossibilities
virtual bool canSet(void) const override
Can this option be set?
virtual bool isMultiple(void) const override
Whether this option is an OptionMultiple.
OptionPossibilities possibilities
virtual bool parseAndSetValue(const std::string s) override
From a given string, set the value of this option.
bool operator()(const T v) const
Returns whether the value v had been set as an option.
T operator()(void) const
static bool fromString(const std::string &s, bool &value)
virtual bool canSet(void) const override
Can this option be set?
static bool fromString(const std::string s, u32_t &value)
virtual bool parseAndSetValue(const std::string s) override
From a given string, set the value of this option.
Option(const std::string &name, const std::string &description, T init)
static bool fromString(const std::string s, std::string &value)
virtual bool isBool(void) const override
bool isExplicitlySet
void setValue(T v)