]> git.tdb.fi Git - libs/core.git/blob - source/core/getopt.h
Restore the check that list options must take an argument
[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
10 namespace Msp {
11
12 class usage_error: public std::runtime_error
13 {
14 private:
15         std::string help_;
16
17 public:
18         usage_error(const std::string &w, const std::string &h = std::string()): std::runtime_error(w), help_(h) { }
19         ~usage_error() throw() { }
20
21         const char *help() const throw() { return help_.c_str(); }
22 };
23
24
25 /**
26 Command line option processor.  Both short and long options are supported, with
27 optional and required arguments.  Automatic help text generation is also
28 available.
29
30 Short options begin with a single dash and are identified by a single letter.
31 Multiple short options may be grouped if they take no arguments; for example,
32 the string "-abc" could be interpreted as having the options 'a', 'b' and 'c'.
33 If the option takes an argument and there are unused characters in the argv
34 element, then those characters are interpreted as the argument.  Otherwise the
35 next element is taken as the argument.  An optional argument must be given in
36 the same element.
37
38 Long options begin with a double dash and are identified by an arbitrary
39 string.  An argument can be specified either in the same argv element,
40 separated by an equals sign, or in the next element.  As with short options,
41 an optional argument must be in the same element.
42
43 A single option may have both alternative forms, but must always have at least
44 a long form.  This is to encourage self-documenting options; it's much easier
45 to remember words than letters.
46
47 A built-in --help option is provided and will output a list of options and
48 their associated help texts.  An application may override this by providing
49 its own option with the same name.
50 */
51 class GetOpt
52 {
53 public:
54         enum ArgType
55         {
56                 NO_ARG,
57                 OPTIONAL_ARG,
58                 REQUIRED_ARG
59         };
60
61         class Option
62         {
63         protected:
64                 Option() { }
65         public:
66                 virtual ~Option() { }
67
68                 /// Sets help text for the option.
69                 virtual Option &set_help(const std::string &) = 0;
70
71                 /** Sets help text for the option, with a placeholder metavariable.  The
72                 metavariable is used to denote the argument in the option list. */
73                 virtual Option &set_help(const std::string &, const std::string &) = 0;
74
75                 virtual Option &bind_seen_count(unsigned &) = 0;
76
77                 /// Returns the number of times this option was seen on the command line.
78                 virtual unsigned get_seen_count() const = 0;
79         };
80
81 private:
82         class Store
83         {
84         protected:
85                 Store() { }
86         public:
87                 virtual ~Store() { }
88
89                 virtual Store *clone() const = 0;
90
91                 virtual bool is_list() const = 0;
92                 virtual void store() = 0;
93                 virtual void store(const std::string &) = 0;
94         };
95
96         class OptionImpl: public Option
97         {
98         protected:
99                 char shrt;
100                 std::string lng;
101                 ArgType arg_type;
102                 unsigned seen_count;
103                 unsigned *ext_seen_count;
104                 std::string help;
105                 std::string metavar;
106                 Store *store;
107
108         public:
109                 OptionImpl(char, const std::string &, const Store &, ArgType);
110                 virtual ~OptionImpl();
111
112                 virtual OptionImpl &set_help(const std::string &);
113                 virtual OptionImpl &set_help(const std::string &, const std::string &);
114                 virtual OptionImpl &bind_seen_count(unsigned &);
115                 char get_short() const { return shrt; }
116                 const std::string &get_long() const { return lng; }
117                 ArgType get_arg_type() const { return arg_type; }
118                 const std::string &get_help() const { return help; }
119                 const std::string &get_metavar() const { return metavar; }
120                 virtual unsigned get_seen_count() const { return seen_count; }
121                 void process();
122                 void process(const std::string &);
123         };
124
125         template<typename T>
126         class SimpleStore: public Store
127         {
128         private:
129                 T &data;
130
131         public:
132                 SimpleStore(T &d): data(d) { }
133
134                 virtual SimpleStore *clone() const
135                 { return new SimpleStore(data); }
136
137                 virtual bool is_list() const { return false; }
138
139                 virtual void store() { }
140
141                 virtual void store(const std::string &a)
142                 { data = lexical_cast<T>(a); }
143         };
144
145         template<typename T>
146         class ListStore: public Store
147         {
148         private:
149                 T &data;
150
151         public:
152                 ListStore(T &d): data(d) { }
153
154                 virtual ListStore *clone() const
155                 { return new ListStore(data); }
156
157                 virtual bool is_list() const { return true; }
158
159                 virtual void store() { }
160
161                 virtual void store(const std::string &a)
162                 { data.push_back(lexical_cast<typename T::value_type>(a)); }
163         };
164
165         typedef std::list<OptionImpl *> OptionList;
166
167         bool help;
168         OptionList opts;
169         std::vector<std::string> args;
170
171 public:
172         GetOpt();
173         ~GetOpt();
174
175         /// Returns any non-option arguments encountered during processing.
176         const std::vector<std::string> &get_args() const { return args; }
177
178         /** Adds an option with both short and long forms.  Processing depends on
179         the type of the destination variable and whether an argument is taken or
180         not.  With an argument, the value is lexical_cast to appropriate type and
181         stored in the destination.  Without an argument, a bool will be set to true
182         and an unsigned will be incremented; any other type will be ignored. */
183         template<typename T>
184         Option &add_option(char s, const std::string &l, T &d, ArgType a = NO_ARG)
185         { return add_option(s, l, SimpleStore<T>(d), a); }
186
187         /** Adds an option with both short and long forms.  The option may be
188         specified multiple times, and the argument from each occurrence is stored in
189         the list.  The argument type must be REQUIRED_ARG. */
190         template<typename T>
191         Option &add_option(char s, const std::string &l, std::list<T> &d, ArgType a = REQUIRED_ARG)
192         { return add_option(s, l, ListStore<std::list<T> >(d), a); }
193
194         /** Adds an option with only a long form. */
195         template<typename T>
196         Option &add_option(const std::string &l, T &d, ArgType a)
197         { return add_option(0, l, d, a); }
198
199 private:
200         OptionImpl &add_option(char, const std::string &, const Store &, ArgType);
201
202         OptionImpl &get_option(char);
203         OptionImpl &get_option(const std::string &);
204
205 public:
206         /** Processes argc/argv style command line arguments.  The contents of argv
207         will be unchanged; use get_args to access non-option arguments. */
208         void operator()(unsigned, const char *const *);
209
210 private:
211         /** Processes a long option.  Returns the number of arguments eaten. */
212         unsigned process_long(const char *const *);
213
214         /** Processes short options.  Returns the number of arguments eaten. */
215         unsigned process_short(const char *const *);
216
217 public:
218         /** Generates a single line that describes known options. */
219         std::string generate_usage(const std::string &) const;
220
221         /** Generates help for known options in tabular format, one option per
222         line.  The returned string will have a linefeed at the end. */
223         std::string generate_help() const;
224 };
225
226 template<> inline void GetOpt::SimpleStore<bool>::store()
227 { data = true; }
228
229 template<> inline void GetOpt::SimpleStore<unsigned>::store()
230 { ++data; }
231
232 template<> inline void GetOpt::SimpleStore<std::string>::store(const std::string &a)
233 { data = a; }
234
235 template<> inline void GetOpt::ListStore<std::list<std::string> >::store(const std::string &a)
236 { data.push_back(a); }
237
238 } // namespace Msp
239
240 #endif