-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathargparser.hpp
More file actions
297 lines (253 loc) · 8.03 KB
/
argparser.hpp
File metadata and controls
297 lines (253 loc) · 8.03 KB
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
/**
* Header only command line argument parser.
*/
#ifndef ARGPARSER_ARGPARSER_HPP_
#define ARGPARSER_ARGPARSER_HPP_
#include <algorithm>
#include <iomanip>
#include <numeric>
#include <sstream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <vector>
namespace argp
{
struct split_options;
class OptionBase;
class PositionalOptionBase;
class KeywordOptionBase;
template <class T>
class PositionalOption;
template <class T>
class KeywordOption;
using OptionsList = std::vector<OptionBase *>;
struct split_options
{
enum Type
{
POSITIONAL,
KEYWORD
};
std::vector<PositionalOptionBase *> positional;
std::vector<KeywordOptionBase *> keyword;
split_options(const OptionsList &options);
};
/**
* This class defines interface that all command line options must use.
*
* Description of fields:
* identifiers - vector of strings that should be matched to this option
* help - string describing the option
* is_set_ - value indicating, if the default value was overwritten (is set even
* if the new value is the same as the default)
*/
class OptionBase
{
protected:
bool is_set_;
/**
* from_string
*
* Subclasses overriding this method must parse the options from the list of
* string views in the parameter. After that, they are expected to be set
* according to the options passed.
*
* First string in the vector is the identifier, that was matched, then
* follows N strings, as requested by get_param_count method.
*
* This method should throw std::invalid_argument exception if the
* conversion could not be done for any reason.
*/
virtual void from_string(const std::vector<std::string_view> &strings) = 0;
public:
OptionBase();
virtual ~OptionBase(){};
/**
* parse
*
* This method is a public wrapper for from_string method that ensures
* the is_set_ field is filled after parsing the parameters.
*/
void parse(const std::vector<std::string_view> &strings);
/**
* get_param_count
*
* This method is used for requesting the number of parameters required to
* initialize the option object.
*
* return value:
* - non-negative value - the number of additional parameters required
* - `-1` - this options requires all remaining arguments
*/
virtual int get_param_count() = 0;
/**
* matches
*
* This method tells the parser, if the option matches the identifier from
* command line. Returning false continues search for matching option. If
* the methods returns true, the parse method of the object is called with
* the right argument count.
*/
virtual bool matches(std::string_view identifier) = 0;
/**
* get_help
*
* Returns two strings -- first should be the name / identifiers of the
* option, second should be a brief description.
*/
virtual std::pair<std::string, std::string> get_help() const = 0;
virtual split_options::Type get_type() const = 0;
bool is_set() const;
};
/**
* Base class for defining positional arguments.
*
* When parsing, all keyword arguments (returning KEYWORD from get_type) are
* tried before calling matches on any subclass of this class.
*/
class PositionalOptionBase : public OptionBase
{
protected:
std::string name;
std::string help;
bool is_required;
public:
PositionalOptionBase(std::string name, std::string help, bool is_required);
virtual std::pair<std::string, std::string> get_help() const override;
virtual split_options::Type get_type() const override;
};
/**
* Base class for defining keyword arguments.
*/
class KeywordOptionBase : public OptionBase
{
protected:
std::vector<std::string> identifiers;
std::string help;
public:
KeywordOptionBase(std::vector<std::string> identifiers, std::string help);
virtual std::pair<std::string, std::string> get_help() const override;
virtual split_options::Type get_type() const override;
};
/**
* This class declares a simple positional argument. It tries to parse the first
* argument that it is given.
*
* It converts a single string parameter to the type specified in the template
* parameter. It uses stream extraction operator to make this conversion, if the
* type does not support this operator, you must create either it,
* a specialization of this template or separate class to parse it.
*
* There are also specializations for these types:
* - std::string - convert the whole parameter to the string
*/
template <class T>
class PositionalOption : public PositionalOptionBase
{
protected:
T val;
virtual void from_string(
const std::vector<std::string_view> &strings) override;
public:
PositionalOption(std::string name, std::string help, bool is_required,
T val = T());
virtual int get_param_count() override;
virtual bool matches(std::string_view) override;
T get_val() const;
};
/**
* This class declares a simple keyword argument. It matches arguments by
* comparing the command line arguments with the values stored in identifiers
* field.
*
* It converts a single string parameter to the type specified in the template
* parameter. It uses stream extraction operator to make this conversion, if the
* type does not support this operator, you must create either it,
* a specialization of this template or separate class to parse it.
*
* There are also specializations for these types:
* - std::string - convert the whole parameter to the string
* - bool - no additional parameters required, false by default, true if found
*/
template <class T>
class KeywordOption : public KeywordOptionBase
{
protected:
T val;
static const int NUM_OPTS;
virtual void from_string(
const std::vector<std::string_view> &strings) override;
public:
KeywordOption(std::vector<std::string> identifiers, std::string help,
T val = T());
virtual int get_param_count() override;
virtual bool matches(std::string_view identifier) override;
T get_val() const;
};
namespace impl
{
/**
* match_option
*
* Internal function for finding if any option matches the curent argument
* parsed.
*
* return value:
* - matching option or nullptr if no option is matched
*/
OptionBase *match_option(const char *arg, const OptionsList &opts);
/**
* handle_match
*
* Internal function that calls option's parse method with the right number
* of arguments when it is found.
*/
void handle_match(int &i, OptionBase *opt, int argc, const char *argv[]);
class indented
{
private:
std::string_view str;
size_t width;
char fill;
public:
indented(std::string_view str, size_t width, char fill = ' ');
friend std::ostream &operator<<(std::ostream &os, const indented &val);
};
} // namespace impl
/**
* parse
*
* Parse string array according to the options given in the argument.
*
* argc - number of c-style strings in argv
* argv - array of c-style strings to parse
*
* opts - vector of options to be parsed from the input
*
* skip_first_n - Ignore this number of strings at the start of argv.
* Default value is set to 1 because C/C++ command line arguments
* start with program name.
*
* return - true if all arguments were recognised
* - false if some arguments were not recognised (these can be
* accessed by calling get_unrecognised method)
*/
std::vector<std::string> parse(int argc, const char *argv[],
const OptionsList &opts, int skip_first_n = 1);
/**
* print_help
*
* Output help to a specified output stream.
*
* os - output stream
* cmd - command to be printed in the first line (`Usage: <cmd> <params>`)
* opts - options for which the help should be printed
* min_w - minimal number of characters that will be used to display
* identifier of an option. Used for aligning help strings.
*/
void print_help(std::ostream &os, std::string_view cmd, const OptionsList &opts,
size_t min_w = 25);
} // namespace argp
#include "argparser.tpp"
#endif // ARGPARSER_ARGPARSER_HPP_