]> git.tdb.fi Git - libs/core.git/blob - source/core/getopt.h
Allow std::vector to be used for GetOpt value lists
[libs/core.git] / source / core / getopt.h
1 #ifndef MSP_CORE_GETOPT_H_
2 #define MSP_CORE_GETOPT_H_
3
4 #include <list>
5 #include <stdexcept>
6 #include <string>
7 #include <vector>
8 #include <msp/strings/lexicalcast.h>
9 #include "noncopyable.h"
10
11 namespace Msp {
12
13 class usage_error: public std::runtime_error
14 {
15 private:
16         std::string help_;
17
18 public:
19         usage_error(const std::string &w, const std::string &h = std::string()): std::runtime_error(w), help_(h) { }
20         ~usage_error() throw() { }
21
22         const char *help() const throw() { return help_.c_str(); }
23 };
24
25
26 /**
27 Command line option processor.  Both short and long options are supported, with
28 optional and required arguments.  Automatic help text generation is also
29 available.
30
31 Short options begin with a single dash and are identified by a single letter.
32 Multiple short options may be grouped if they take no arguments; for example,
33 the string "-abc" could be interpreted as having the options 'a', 'b' and 'c'.
34 If the option takes an argument and there are unused characters in the argv
35 element, then those characters are interpreted as the argument.  Otherwise the
36 next element is taken as the argument.  An optional argument must be given in
37 the same element if it is given.
38
39 Long options begin with a double dash and are identified by an arbitrary
40 string.  An argument can be specified either in the same argv element,
41 separated by an equals sign, or in the next element.  As with short options,
42 an optional argument, if given, must be in the same element.
43
44 A single option may have both alternative forms, but must always have at least
45 a long form.  This is to encourage self-documenting options; it's much easier
46 to remember words than letters.
47
48 Positional arguments are also supported.  They are identified by an arbitrary
49 string, but the identifier is only used in help text and error messages.  Any
50 number of the final arguments may be optional.
51
52 To support applications that take an arbitrary amount of arguments, a single
53 positional argument list can be specified.  Fixed positional arguments are
54 allowed together with a list, but they can't be optional.  An application that
55 wants to do complex processing on the argument list can declare a list of
56 string arguments.
57
58 A built-in --help option is provided and will output a list of options,
59 arguments and their associated help texts.  An application may override this by
60 providing its own option with the same name.
61 */
62 class GetOpt: private NonCopyable
63 {
64 public:
65         enum ArgType
66         {
67                 NO_ARG,
68                 OPTIONAL_ARG,
69                 REQUIRED_ARG
70         };
71
72         class Option
73         {
74         protected:
75                 Option() { }
76         public:
77                 virtual ~Option() { }
78
79                 /// Sets help text for the option.
80                 virtual Option &set_help(const std::string &) = 0;
81
82                 /** Sets help text for the option, with a placeholder metavariable.  The
83                 metavariable is used to denote the argument in the option list. */
84                 virtual Option &set_help(const std::string &, const std::string &) = 0;
85
86                 virtual Option &bind_seen_count(unsigned &) = 0;
87
88                 /// Returns the number of times this option was seen on the command line.
89                 virtual unsigned get_seen_count() const = 0;
90         };
91
92         class Argument
93         {
94         protected:
95                 Argument() { }
96         public:
97                 virtual ~Argument() { }
98
99                 virtual Argument &set_help(const std::string &) = 0;
100         };
101
102 private:
103         class Store
104         {
105         protected:
106                 Store() { }
107         public:
108                 virtual ~Store() { }
109
110                 virtual Store *clone() const = 0;
111
112                 virtual bool is_list() const = 0;
113                 virtual void store() = 0;
114                 virtual void store(const std::string &) = 0;
115         };
116
117         class OptionImpl: public Option
118         {
119         protected:
120                 char shrt;
121                 std::string lng;
122                 ArgType arg_type;
123                 unsigned seen_count;
124                 unsigned *ext_seen_count;
125                 std::string help;
126                 std::string metavar;
127                 Store *store;
128
129         public:
130                 OptionImpl(char, const std::string &, const Store &, ArgType);
131                 virtual ~OptionImpl();
132
133                 virtual OptionImpl &set_help(const std::string &);
134                 virtual OptionImpl &set_help(const std::string &, const std::string &);
135                 virtual OptionImpl &bind_seen_count(unsigned &);
136                 char get_short() const { return shrt; }
137                 const std::string &get_long() const { return lng; }
138                 ArgType get_arg_type() const { return arg_type; }
139                 const std::string &get_help() const { return help; }
140                 const std::string &get_metavar() const { return metavar; }
141                 virtual unsigned get_seen_count() const { return seen_count; }
142                 void process();
143                 void process(const std::string &);
144         };
145
146         class ArgumentImpl: public Argument
147         {
148         private:
149                 std::string name;
150                 ArgType type;
151                 std::string help;
152                 Store *store;
153
154         public:
155                 ArgumentImpl(const std::string &, const Store &, ArgType);
156                 virtual ~ArgumentImpl();
157
158                 virtual ArgumentImpl &set_help(const std::string &);
159                 const std::string &get_name() const { return name; }
160                 ArgType get_type() const { return type; }
161                 const std::string &get_help() const { return help; }
162                 bool is_list_store() const { return store->is_list(); }
163                 void process(const std::string &);
164         };
165
166         template<typename T>
167         class SimpleStore: public Store
168         {
169         private:
170                 T &data;
171
172         public:
173                 SimpleStore(T &d): data(d) { }
174
175                 virtual SimpleStore *clone() const
176                 { return new SimpleStore(data); }
177
178                 virtual bool is_list() const { return false; }
179
180                 virtual void store() { }
181
182                 virtual void store(const std::string &a)
183                 { data = lexical_cast<T>(a); }
184         };
185
186         template<typename T>
187         class ListStore: public Store
188         {
189         private:
190                 T &data;
191
192         public:
193                 ListStore(T &d): data(d) { }
194
195                 virtual ListStore *clone() const
196                 { return new ListStore(data); }
197
198                 virtual bool is_list() const { return true; }
199
200                 virtual void store() { }
201
202                 virtual void store(const std::string &a)
203                 { data.push_back(lexical_cast<typename T::value_type>(a)); }
204         };
205
206         typedef std::list<OptionImpl *> OptionList;
207         typedef std::list<ArgumentImpl *> ArgumentList;
208
209         bool help;
210         OptionList opts;
211         ArgumentList args;
212         std::vector<std::string> args_raw;
213
214 public:
215         GetOpt();
216         ~GetOpt();
217
218         /** Returns any non-option arguments encountered during processing.
219         Deprecated. */
220         const std::vector<std::string> &get_args() const { return args_raw; }
221
222         /** Adds an option with both short and long forms.  Processing depends on
223         the type of the destination variable and whether an argument is taken or
224         not.  With an argument, the value is lexical_cast to the appropriate type
225         and stored in the destination.  Without an argument, a bool will be set to
226         true and an unsigned will be incremented; any other type will be ignored. */
227         template<typename T>
228         Option &add_option(char s, const std::string &l, T &d, ArgType a = NO_ARG)
229         { return add_option(s, l, SimpleStore<T>(d), a); }
230
231         /** Adds an option with both short and long forms.  The option may be
232         specified multiple times, and the argument from each occurrence is stored in
233         the list.  The argument type must be REQUIRED_ARG. */
234         template<typename T>
235         Option &add_option(char s, const std::string &l, std::vector<T> &d, ArgType a = REQUIRED_ARG)
236         { return add_option(s, l, ListStore<std::vector<T> >(d), a); }
237
238         /** Adds an option with both short and long forms.  The option may be
239         specified multiple times, and the argument from each occurrence is stored in
240         the list.  The argument type must be REQUIRED_ARG. */
241         template<typename T>
242         Option &add_option(char s, const std::string &l, std::list<T> &d, ArgType a = REQUIRED_ARG)
243         { return add_option(s, l, ListStore<std::list<T> >(d), a); }
244
245         /** Adds an option with only a long form. */
246         template<typename T>
247         Option &add_option(const std::string &l, T &d, ArgType a)
248         { return add_option(0, l, d, a); }
249
250         /** Adds a positional argument.  The value will be lexical_cast to the
251         appropriate type and stored in the destination. */
252         template<typename T>
253         Argument &add_argument(const std::string &n, T &d, ArgType a = REQUIRED_ARG)
254         { return add_argument(n, SimpleStore<T>(d), a); }
255
256         /** Adds a positional argument list.  If the list is declared as required,
257         at least one element must be given; an optional list may be empty.  Only one
258         list may be added, and optional fixed arguments can't be used with it. */
259         template<typename T>
260         Argument &add_argument(const std::string &n, std::vector<T> &d, ArgType a = REQUIRED_ARG)
261         { return add_argument(n, ListStore<std::vector<T> >(d), a); }
262
263         /** Adds a positional argument list.  If the list is declared as required,
264         at least one element must be given; an optional list may be empty.  Only one
265         list may be added, and optional fixed arguments can't be used with it. */
266         template<typename T>
267         Argument &add_argument(const std::string &n, std::list<T> &d, ArgType a = REQUIRED_ARG)
268         { return add_argument(n, ListStore<std::list<T> >(d), a); }
269
270 private:
271         OptionImpl &add_option(char, const std::string &, const Store &, ArgType);
272         ArgumentImpl &add_argument(const std::string &, const Store &, ArgType);
273
274         OptionImpl &get_option(char);
275         OptionImpl &get_option(const std::string &);
276
277 public:
278         /** Processes argc/argv style command line arguments.  The contents of argv
279         will be unchanged; use get_args to access non-option arguments. */
280         void operator()(unsigned, const char *const *);
281
282 private:
283         /** Processes a long option.  Returns the number of arguments eaten. */
284         unsigned process_long(const char *const *);
285
286         /** Processes short options.  Returns the number of arguments eaten. */
287         unsigned process_short(const char *const *);
288
289 public:
290         /** Generates a single line that describes known options and arguments.  If
291         compact is true, the options list is replaced with a placeholder.  This
292         provides cleaner output if full help text is printed. */
293         std::string generate_usage(const std::string &, bool compact = false) const;
294
295         /** Generates help for known options and arguments in tabular format, one
296         item per line.  The returned string will have a linefeed at the end. */
297         std::string generate_help() const;
298 };
299
300 template<> inline void GetOpt::SimpleStore<bool>::store()
301 { data = true; }
302
303 template<> inline void GetOpt::SimpleStore<unsigned>::store()
304 { ++data; }
305
306 template<> inline void GetOpt::SimpleStore<std::string>::store(const std::string &a)
307 { data = a; }
308
309 template<> inline void GetOpt::ListStore<std::vector<std::string> >::store(const std::string &a)
310 { data.push_back(a); }
311
312 template<> inline void GetOpt::ListStore<std::list<std::string> >::store(const std::string &a)
313 { data.push_back(a); }
314
315 } // namespace Msp
316
317 #endif