-
Notifications
You must be signed in to change notification settings - Fork 0
/
parser.hpp
345 lines (334 loc) · 10.3 KB
/
parser.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
#pragma once
#include <string>
#include <functional>
#include <vector>
#include <map>
#include <exception>
#include <iostream>
#define ARG_SEPARATOR ','
namespace ArgParse{
/**
* @brief this will be thrown when a parser error is thrown. This class is
* only an inteface for specific parser errors. Use this to catch most parse
* errors
*/
class ParserError : public std::exception{
public:
virtual const char* what() const noexcept;
};
/**
* @brief thrown for miscellanenous uncategorized errors or unexpected errors
* that can happen when parsing the content of argv.
*/
class GenericParserError : public ParserError{
public:
GenericParserError(std::string&& msg);
const char* what() const noexcept;
private:
std::string _msg;
};
/**
* @brief Thrown to print out help message
*/
class PrintHelp{};
/**
* @brief Thrown when an invalid key is found in the parser
*/
class InvalidKey : public ParserError{
public:
InvalidKey(const std::string& key);
const char* what() const noexcept;
private:
std::string _msg;
};
/**
* @brief thrown when a key requested is not in the parsed results.
*/
class OutOfBounds : public ParserError{
public:
OutOfBounds(size_t pos);
OutOfBounds(const std::string& key);
const char* what() const noexcept;
private:
std::string _msg;
};
/**
* @brief an internal data structure to handle the data keps in Arguments.
* This structure specifically only keeps the data.
*/
class ArgsData{
public:
void set(const std::string& data);
const std::string& get() const;
void append(const std::string& data);
std::vector<std::string> splitBySeparator(char sep) const;
void clear();
private:
std::string _data;
bool _isInitialized = false;
};
/**
* @brief a data structure that holds the data structure to keep the data for
* arguments.
*/
class Args{
public:
Args(
std::string&& helpString = "", bool required = true, bool many = false,
std::string&& defaultValue = ""
);
const std::string& data() const;
const std::string& defaultValue() const;
const std::string& helpString() const;
bool isMultiple() const;
bool isRequired() const;
bool isInitialized() const;
void appendOrSet(const std::string& data);
void clear();
// Split out
template<typename Target>
std::vector<Target> convert(char sep) const;
template<typename Target>
Target convert() const;
private:
std::string _helpString;
std::string _default;
ArgsData _data;
bool _required;
bool _many;
};
/**
* @brief a parser that can be used to parse arguments given from the command
* line. Call parse(argc, argv, ...) to parse arguments given from the command
* line
*/
class Parser{
using Key = std::string;
public:
/* Adding arguments */
template<typename... T>
/**
* @brief adds a sequential argument to the parser
*/
void addSeqArgument(T&& ...args);
template<typename... T>
/**
* @brief adds a keyed argument to the parser
* @param key the key to use, including "--", for example : "--file",
* this decision to include "--" is purely for ease of use
*/
void addArgument(Key&& key, T&& ...args);
void getHelpString(std::ostream& stream) const;
/**
* @brief Parses the contents of argv into the parser
* @param argc argument count received from the main function
* @param argv argument values received from the main function
* @param exitOrException if the function should exit immediately or throw
* and exception when any parsing error happens. True is to exit directly
* @param printHelp if the help message should be printed when any error
* is encountered before the function exits or an exception is thrown. If
* --help or -h is invoked this option will be ignored and the help will
* be printed anyway and exit will be invoked.
*/
void parse(
int argc, char** argv, bool exitOrException = true,bool printHelp = true
);
/*
Obtaining the content with conversion
*/
const std::string& get(size_t pos) const;
const std::string& get(const Key& key) const;
template<typename T>
T get(size_t pos) const;
template<typename T>
T get(const Key& key) const;
template<typename T>
std::vector<T> get(size_t pos, char sep) const;
template<typename T>
std::vector<T> get(const Key& key, char sep) const;
/*
Helper Functions
*/
static bool isValidKey(const Key& key);
static void checkForHelpArgv(int argc, char** argv);
static bool isKwargTag(const std::string& s);
/*
Non Static Helper
*/
bool validateKey(const Key& key) const;
bool doesKeyExist(const Key& key) const;
bool doesPosExist(size_t pos) const;
/*
Operation on the parser itself
*/
void reset(bool keepArg = true);
private:
std::map<Key, Args> _kwargs;
std::vector<Args> _args;
bool _parsed = false;
void _parse(int argc, char** argv);
void _keyExistOrException(const Key& key) const;
void _posExistOrException(size_t pos) const;
void _parsedOrException() const;
Key _keyNameFromKey(const Key& key) const;
void _parseArgv(int argc, char** argv);
void _reset();
void _resetKeepArgument();
};
template<typename T>
inline T Args::convert() const{
return T(_data.get());
}
template<typename T>
inline std::vector<T> Args::convert(char sep) const{
std::vector<T> converted;
auto separated = _data.splitBySeparator(sep);
converted.reserve(separated.size());
std::transform(
separated.begin(), separated.end(), std::back_inserter(converted),
[](const std::string& s){ return T(s);}
);
return converted;
}
// Parser
template<typename... T>
inline void Parser::addSeqArgument(T&& ...args){
_args.emplace_back(std::forward<T>(args)...);
}
template<typename ...T>
inline void Parser::addArgument(Key&& key, T&& ...args){
if (!validateKey(key))
throw InvalidKey(key);
_kwargs.emplace(_keyNameFromKey(key), Args(std::forward<T>(args)...));
}
template<typename T>
inline T Parser::get(size_t pos) const{
_parsedOrException();
_posExistOrException(pos);
return _args.at(pos).convert<T>();
}
template<typename T>
inline T Parser::get(const std::string& key) const{
_parsedOrException();
_keyExistOrException(key);
return _kwargs.at(key).convert<T>();
}
template<typename T>
inline std::vector<T> Parser::get(size_t pos, char sep) const{
_parsedOrException();
_posExistOrException(pos);
const Args& arg = _args.at(pos);
return arg.convert<T>(sep);
}
template<typename T>
inline std::vector<T> Parser::get(const Key& key, char sep) const{
_parsedOrException();
_keyExistOrException(key);
const Args& arg = _kwargs.at(key);
return arg.convert<T>(sep);
}
template<>
inline double Args::convert<double>() const{
return std::atof(_data.get().c_str());
}
template<>
inline int Args::convert<int>() const{
return std::atoi(_data.get().c_str());
}
template<>
inline bool Args::convert<bool>() const{
char firstCharacter = _data.get().at(0);
return firstCharacter == 't' || firstCharacter == 'T';
}
template<>
inline uint32_t Args::convert<uint32_t>() const{
return static_cast<uint32_t>(convert<int>());
}
template<>
inline size_t Args::convert<size_t>() const{
return static_cast<size_t>(convert<int>());
}
template<>
inline float Args::convert<float>() const{
return static_cast<float>(convert<double>());
}
template<>
inline std::vector<double> Args::convert<double>(char sep) const{
std::vector<double> converted;
auto separated = _data.splitBySeparator(sep);
converted.reserve(separated.size());
std::transform(
separated.begin(), separated.end(), std::back_inserter(converted),
[](const std::string& s){
return std::atof(s.c_str());
}
);
return converted;
}
template<>
inline std::vector<int> Args::convert<int>(char sep) const{
std::vector<int> converted;
auto separated = _data.splitBySeparator(sep);
converted.reserve(separated.size());
std::transform(
separated.begin(), separated.end(), std::back_inserter(converted),
[](const std::string& s){
return std::atoi(s.c_str());
}
);
return converted;
}
template<>
inline std::vector<bool> Args::convert<bool>(char sep) const{
std::vector<bool> converted;
auto separated = _data.splitBySeparator(sep);
converted.reserve(separated.size());
std::transform(
separated.begin(), separated.end(), std::back_inserter(converted),
[](const std::string& s){
char firstCharacter = s.at(0);
return firstCharacter == 't' || firstCharacter == 'T';
}
);
return converted;
}
template<>
inline std::vector<uint32_t> Args::convert<uint32_t>(char sep) const{
std::vector<uint32_t> converted;
auto separated = _data.splitBySeparator(sep);
converted.reserve(separated.size());
std::transform(
separated.begin(), separated.end(), std::back_inserter(converted),
[](const std::string& s){
return static_cast<uint32_t>(std::atoi(s.c_str()));
}
);
return converted;
}
template<>
inline std::vector<size_t> Args::convert<size_t>(char sep) const{
std::vector<size_t> converted;
auto separated = _data.splitBySeparator(sep);
converted.reserve(separated.size());
std::transform(
separated.begin(), separated.end(), std::back_inserter(converted),
[](const std::string& s){
return static_cast<size_t>(std::atoi(s.c_str()));
}
);
return converted;
}
template<>
inline std::vector<float> Args::convert<float>(char sep) const{
std::vector<float> converted;
auto separated = _data.splitBySeparator(sep);
converted.reserve(separated.size());
std::transform(
separated.begin(), separated.end(), std::back_inserter(converted),
[](const std::string& s){
return static_cast<float>(std::atof(s.c_str()));
}
);
return converted;
}
}