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