1 // Copyright (C) 2006 Timothy Brownawell <tbrownaw@gmail.com>
2 //
3 // This program is made available under the GNU GPL version 2.0 or
4 // greater. See the accompanying file COPYING for details.
5 //
6 // This program is distributed WITHOUT ANY WARRANTY; without even the
7 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8 // PURPOSE.
9
10 #ifndef __OPTION_HH__
11 #define __OPTION_HH__
12
13 /*
14 * Infrastructure for parsing options.
15 *
16 * This can be used on its own with concrete_option_set::operator()(), or
17 * used with something like options.{cc,hh} and option_set. The former is
18 * very simple to do, while the latter should allow slightly better code
19 * structure for more involved uses.
20 */
21
22 #include <stdexcept>
23 #include <map>
24 #include <set>
25 #include "vector.hh"
26
27 #include <boost/function.hpp>
28 #include "lexical_cast.hh"
29
30 #include "sanity.hh"
31 #include "vocab.hh"
32
33 // The types to represent the command line's parameters.
34 class arg_type : public utf8 {
35 public:
arg_type(void)36 explicit arg_type(void) : utf8() {}
arg_type(std::string const & s,origin::type f)37 arg_type(std::string const & s, origin::type f) : utf8(s, f) {}
arg_type(utf8 const & u)38 explicit arg_type(utf8 const & u) : utf8(u) {}
39 };
40 template <>
dump(arg_type const & a,std::string & out)41 inline void dump(arg_type const & a, std::string & out) { out = a(); }
42 typedef std::vector< arg_type > args_vector;
43
44 namespace option {
45 // Base for errors thrown by this code.
46 struct option_error : public std::invalid_argument
47 {
48 option_error(std::string const & str);
49 };
50 struct unknown_option : public option_error
51 {
52 unknown_option(std::string const & opt);
53 };
54 struct missing_arg : public option_error
55 {
56 missing_arg(std::string const & opt);
57 };
58 // -ofoo or --opt=foo when the option doesn't take an argument
59 struct extra_arg : public option_error
60 {
61 extra_arg(std::string const & opt);
62 };
63 // thrown by from_command_line when setting an option fails
64 // by either boost::bad_lexical_cast or bad_arg_internal
65 struct bad_arg : public option_error
66 {
67 bad_arg(std::string const & opt, arg_type const & arg);
68 bad_arg(std::string const & opt,
69 arg_type const & arg,
70 std::string const & reason);
71 };
72 // from_command_line() catches this and boost::bad_lexical_cast
73 // and converts them to bad_arg exceptions
74 struct bad_arg_internal
75 {
76 std::string reason;
77 bad_arg_internal(std::string const & str = "");
78 };
79
80 // Split a "long,s/cancel" option name into long and short names.
81 void splitname(char const * from, std::string & name, std::string & n, std::string & cancelname);
82
83 // An option that can be set and reset.
84 struct concrete_option
85 {
86 char const * description;
87 std::string longname;
88 std::string shortname;
89 std::string cancelname;
90 bool has_arg;
91 boost::function<void (std::string)> setter;
92 boost::function<void ()> resetter;
93 bool hidden;
94 char const * deprecated;
95
96 concrete_option();
97 concrete_option(char const * names,
98 char const * desc,
99 bool arg,
100 boost::function<void (std::string)> set,
101 boost::function<void ()> reset,
102 bool hide = false,
103 char const * deprecate = 0);
104
105 bool operator<(concrete_option const & other) const;
106 };
107
108 // A group of options, which can be set from a command line
109 // and can produce a usage string.
110 struct concrete_option_set
111 {
112 std::set<concrete_option> options;
113 concrete_option_set();
114 concrete_option_set(std::set<concrete_option> const & other);
115 concrete_option_set(concrete_option const & opt);
116
117 // for building a concrete_option_set directly (as done in unit_tests.cc),
118 // rather than using intermediate machinery like in options*
119 concrete_option_set &
120 operator()(char const * names,
121 char const * desc,
122 boost::function<void ()> set,
123 boost::function<void ()> reset = 0,
124 bool hide = false,
125 char const * deprecate = 0);
126 concrete_option_set &
127 operator()(char const * names,
128 char const * desc,
129 boost::function<void (std::string)> set,
130 boost::function<void ()> reset = 0,
131 bool hide = false,
132 char const * deprecate = 0);
133
134 concrete_option_set operator | (concrete_option_set const & other) const;
135 void reset() const;
136 void get_usage_strings(std::vector<std::string> & names,
137 std::vector<std::string> & descriptions,
138 unsigned int & maxnamelen,
139 bool show_hidden
140 /*no way to see deprecated*/) const;
141
142 enum preparse_flag { no_preparse, preparse };
143 void from_command_line(args_vector & args,
144 preparse_flag pf = no_preparse);
145 // Does not allow --xargs
146 void from_command_line(int argc,
147 char const * const * argv);
148 typedef std::pair<std::string, std::string> key_value_pair;
149 typedef std::vector<key_value_pair> key_value_list;
150 // Does not allow --xargs
151 void from_key_value_pairs(key_value_list const & keyvals);
152 };
153 concrete_option_set
154 operator | (concrete_option const & a, concrete_option const & b);
155
156 // used by the setter() functions below
157 template<typename T>
158 struct setter_class
159 {
160 T & item;
setter_classoption::setter_class161 setter_class(T & i)
162 : item(i)
163 {}
operator ()option::setter_class164 void operator()(std::string s)
165 {
166 item = boost::lexical_cast<T>(s);
167 }
168 };
169 template<>
170 struct setter_class<bool>
171 {
172 bool & item;
setter_classoption::setter_class173 setter_class(bool & i)
174 : item(i)
175 {}
operator ()option::setter_class176 void operator()()
177 {
178 item = true;
179 }
180 };
181 template<typename T>
182 struct setter_class<std::vector<T> >
183 {
184 std::vector<T> & items;
setter_classoption::setter_class185 setter_class(std::vector<T> & i)
186 : items(i)
187 {}
operator ()option::setter_class188 void operator()(std::string s)
189 {
190 items.push_back(boost::lexical_cast<T>(s));
191 }
192 };
193 template<typename T>
194 struct resetter_class
195 {
196 T & item;
197 T value;
resetter_classoption::resetter_class198 resetter_class(T & i, T const & v)
199 : item(i), value(v)
200 {}
operator ()option::resetter_class201 void operator()()
202 {
203 item = value;
204 }
205 };
206
207 // convenience functions to generate a setter for a var
208 template<typename T> inline
setter(T & item)209 boost::function<void(std::string)> setter(T & item)
210 {
211 return setter_class<T>(item);
212 }
setter(bool & item)213 inline boost::function<void()> setter(bool & item)
214 {
215 return setter_class<bool>(item);
216 }
217 // convenience function to generate a resetter for a var
218 template<typename T> inline
resetter(T & item,T const & value=T ())219 boost::function<void()> resetter(T & item, T const & value = T())
220 {
221 return resetter_class<T>(item, value);
222 }
223
224 // because std::bind1st can't handle producing a nullary functor
225 template<typename T>
226 struct binder_only
227 {
228 T * obj;
229 boost::function<void(T*)> fun;
binder_onlyoption::binder_only230 binder_only(boost::function<void(T*)> const & f, T * o)
231 : obj(o), fun(f)
232 {}
operator ()option::binder_only233 void operator()()
234 {
235 fun(obj);
236 }
237 };
238
239 // Options that need to be attached to some other object
240 // in order for set and reset to be meaningful.
241 template<typename T>
242 struct option
243 {
244 char const * description;
245 char const * names;
246 bool has_arg;
247 boost::function<void (T*, std::string)> setter;
248 boost::function<void (T*)> resetter;
249 bool hidden;
250 char const * deprecated;
251
optionoption::option252 option(char const * name,
253 char const * desc,
254 bool arg,
255 void(T::*set)(std::string),
256 void(T::*reset)(),
257 bool hide,
258 char const * deprecate)
259 {
260 I((name && name[0]) || (desc && desc[0]));
261 description = desc;
262 names = name;
263 has_arg = arg;
264 setter = set;
265 resetter = reset;
266 hidden = hide;
267 deprecated = deprecate;
268 }
269
instantiateoption::option270 concrete_option instantiate(T * obj) const
271 {
272 concrete_option out;
273 out.description = description;
274 splitname(names, out.longname, out.shortname, out.cancelname);
275 out.has_arg = has_arg;
276
277 if (setter)
278 out.setter = std::bind1st(setter, obj);
279 if (resetter)
280 out.resetter = binder_only<T>(resetter, obj);
281
282 out.hidden = hidden;
283 out.deprecated = deprecated;
284
285 return out;
286 }
287
operator <option::option288 bool operator<(option const & other) const
289 {
290 if (names != other.names)
291 return names < other.names;
292 return description < other.description;
293 }
294 };
295
296 // A group of unattached options, which can be given an object
297 // to attach themselves to.
298 template<typename T>
299 struct option_set
300 {
301 std::set<option<T> > options;
option_setoption::option_set302 option_set(){}
option_setoption::option_set303 option_set(option_set<T> const & other)
304 : options(other.options)
305 {}
option_setoption::option_set306 option_set(option<T> const & opt)
307 {
308 options.insert(opt);
309 }
310
option_setoption::option_set311 option_set(char const * name,
312 char const * desc,
313 bool arg,
314 void(T::*set)(std::string),
315 void(T::*reset)(),
316 bool hidden = false,
317 char const * deprecated = 0)
318 {
319 options.insert(option<T>(name, desc, arg, set, reset, hidden, deprecated));
320 }
instantiateoption::option_set321 concrete_option_set instantiate(T * obj) const
322 {
323 std::set<concrete_option> out;
324 for (typename std::set<option<T> >::const_iterator i = options.begin();
325 i != options.end(); ++i)
326 out.insert(i->instantiate(obj));
327 return out;
328 }
operator |option::option_set329 option_set<T> operator | (option_set<T> const & other) const
330 {
331 option_set<T> combined;
332 std::set_union(options.begin(), options.end(),
333 other.options.begin(), other.options.end(),
334 std::inserter(combined.options, combined.options.begin()));
335 return combined;
336 }
operator -option::option_set337 option_set<T> operator - (option_set<T> const & other) const
338 {
339 option_set<T> combined;
340 std::set_difference(options.begin(), options.end(),
341 other.options.begin(), other.options.end(),
342 std::inserter(combined.options,
343 combined.options.begin()));
344 return combined;
345 }
emptyoption::option_set346 bool empty() const {return options.empty();}
347 };
348 template<typename T>
349 option_set<T>
operator |(option<T> const & a,option<T> const & b)350 operator | (option<T> const & a, option<T> const & b)
351 {
352 return option_set<T>(a) | b;
353 }
354
355 }
356
357
358 #endif
359
360 // Local Variables:
361 // mode: C++
362 // fill-column: 76
363 // c-file-style: "gnu"
364 // indent-tabs-mode: nil
365 // End:
366 // vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
367