1 | #ifndef __OPTION_HH__␊ |
2 | #define __OPTION_HH__␊ |
3 | ␊ |
4 | #include <stdexcept>␊ |
5 | #include <map>␊ |
6 | #include <set>␊ |
7 | #include "vector.hh"␊ |
8 | ␊ |
9 | #include <boost/function.hpp>␊ |
10 | #include "lexical_cast.hh"␊ |
11 | ␊ |
12 | #include "sanity.hh"␊ |
13 | #include "vocab.hh"␊ |
14 | ␊ |
15 | // The types to represent the command line's parameters.␊ |
16 | class arg_type : public utf8 {␊ |
17 | public:␊ |
18 | explicit arg_type(void) : utf8() {}␊ |
19 | explicit arg_type(std::string const & s) : utf8(s) {}␊ |
20 | explicit arg_type(utf8 const & u) : utf8(u) {}␊ |
21 | };␊ |
22 | template <>␊ |
23 | inline void dump(arg_type const & a, std::string & out) { out = a(); }␊ |
24 | typedef std::vector< arg_type > args_vector;␊ |
25 | ␊ |
26 | namespace option {␊ |
27 | // Base for errors thrown by this code.␊ |
28 | struct option_error : public std::invalid_argument␊ |
29 | {␊ |
30 | option_error(std::string const & str);␊ |
31 | };␊ |
32 | struct unknown_option : public option_error␊ |
33 | {␊ |
34 | unknown_option(std::string const & opt);␊ |
35 | };␊ |
36 | struct missing_arg : public option_error␊ |
37 | {␊ |
38 | missing_arg(std::string const & opt);␊ |
39 | };␊ |
40 | // -ofoo or --opt=foo when the option doesn't take an argument␊ |
41 | struct extra_arg : public option_error␊ |
42 | {␊ |
43 | extra_arg(std::string const & opt);␊ |
44 | };␊ |
45 | // thrown by from_command_line when setting an option fails␊ |
46 | // by either boost::bad_lexical_cast or bad_arg_internal␊ |
47 | struct bad_arg : public option_error␊ |
48 | {␊ |
49 | bad_arg(std::string const & opt, arg_type const & arg);␊ |
50 | bad_arg(std::string const & opt,␊ |
51 | ␉ arg_type const & arg,␊ |
52 | ␉ std::string const & reason);␊ |
53 | };␊ |
54 | // from_command_line() catches this and boost::bad_lexical_cast␊ |
55 | // and converts them to bad_arg exceptions␊ |
56 | struct bad_arg_internal␊ |
57 | {␊ |
58 | std::string reason;␊ |
59 | bad_arg_internal(std::string const & str = "");␊ |
60 | };␊ |
61 | ␊ |
62 | // Split a "long,s" option name into long and short names.␊ |
63 | void splitname(std::string const & from, std::string & name, std::string & n);␊ |
64 | ␊ |
65 | // An option that can be set and reset.␊ |
66 | struct concrete_option␊ |
67 | {␊ |
68 | std::string description;␊ |
69 | std::string longname;␊ |
70 | std::string shortname;␊ |
71 | bool has_arg;␊ |
72 | boost::function<void (std::string)> setter;␊ |
73 | boost::function<void ()> resetter;␊ |
74 | ␊ |
75 | concrete_option();␊ |
76 | concrete_option(std::string const & names,␊ |
77 | ␉␉ std::string const & desc,␊ |
78 | ␉␉ bool arg,␊ |
79 | ␉␉ boost::function<void (std::string)> set,␊ |
80 | ␉␉ boost::function<void ()> reset);␊ |
81 | ␊ |
82 | bool operator<(concrete_option const & other) const;␊ |
83 | };␊ |
84 | ␊ |
85 | // A group of options, which can be set from a command line␊ |
86 | // and can produce a usage string.␊ |
87 | struct concrete_option_set␊ |
88 | {␊ |
89 | std::set<concrete_option> options;␊ |
90 | concrete_option_set();␊ |
91 | concrete_option_set(std::set<concrete_option> const & other);␊ |
92 | concrete_option_set(concrete_option const & opt);␊ |
93 | ␊ |
94 | // for building a concret_option_set directly (as done in unit_tests.cc),␊ |
95 | // rather than using intermediate machinery like in options*␊ |
96 | concrete_option_set &␊ |
97 | operator()(std::string const & names,␊ |
98 | ␉ std::string const & desc,␊ |
99 | ␉ boost::function<void ()> set,␊ |
100 | ␉ boost::function<void ()> reset = 0);␊ |
101 | concrete_option_set &␊ |
102 | operator()(std::string const & names,␊ |
103 | ␉ std::string const & desc,␊ |
104 | ␉ boost::function<void (std::string)> set,␊ |
105 | ␉ boost::function<void ()> reset = 0);␊ |
106 | ␊ |
107 | concrete_option_set operator | (concrete_option_set const & other) const;␊ |
108 | void reset() const;␊ |
109 | std::string get_usage_str() const;␊ |
110 | void from_command_line(args_vector & args, bool allow_xargs = true);␊ |
111 | void from_command_line(int argc, char const * const * argv);␊ |
112 | void from_key_value_pairs(std::vector<std::pair<std::string, std::string> > const & keyvals);␊ |
113 | };␊ |
114 | concrete_option_set␊ |
115 | operator | (concrete_option const & a, concrete_option const & b);␊ |
116 | ␊ |
117 | // used by the setter() functions below␊ |
118 | template<typename T>␊ |
119 | struct setter_class␊ |
120 | {␊ |
121 | T & item;␊ |
122 | setter_class(T & i)␊ |
123 | : item(i)␊ |
124 | {}␊ |
125 | void operator()(std::string s)␊ |
126 | {␊ |
127 | item = boost::lexical_cast<T>(s);␊ |
128 | }␊ |
129 | };␊ |
130 | template<>␊ |
131 | struct setter_class<bool>␊ |
132 | {␊ |
133 | bool & item;␊ |
134 | setter_class(bool & i)␊ |
135 | : item(i)␊ |
136 | {}␊ |
137 | void operator()()␊ |
138 | {␊ |
139 | item = true;␊ |
140 | }␊ |
141 | };␊ |
142 | template<typename T>␊ |
143 | struct setter_class<std::vector<T> >␊ |
144 | {␊ |
145 | std::vector<T> & items;␊ |
146 | setter_class(std::vector<T> & i)␊ |
147 | : items(i)␊ |
148 | {}␊ |
149 | void operator()(std::string s)␊ |
150 | {␊ |
151 | items.push_back(boost::lexical_cast<T>(s));␊ |
152 | }␊ |
153 | };␊ |
154 | template<typename T>␊ |
155 | struct resetter_class␊ |
156 | {␊ |
157 | T & item;␊ |
158 | T value;␊ |
159 | resetter_class(T & i, T const & v)␊ |
160 | : item(i), value(v)␊ |
161 | {}␊ |
162 | void operator()()␊ |
163 | {␊ |
164 | item = value;␊ |
165 | }␊ |
166 | };␊ |
167 | ␊ |
168 | // convenience functions to generate a setter for a var␊ |
169 | template<typename T> inline␊ |
170 | boost::function<void(std::string)> setter(T & item)␊ |
171 | {␊ |
172 | return setter_class<T>(item);␊ |
173 | }␊ |
174 | inline boost::function<void()> setter(bool & item)␊ |
175 | {␊ |
176 | return setter_class<bool>(item);␊ |
177 | }␊ |
178 | // convenience function to generate a resetter for a var␊ |
179 | template<typename T> inline␊ |
180 | boost::function<void()> resetter(T & item, T const & value = T())␊ |
181 | {␊ |
182 | return resetter_class<T>(item, value);␊ |
183 | }␊ |
184 | ␊ |
185 | // because std::bind1st can't handle producing a nullary functor␊ |
186 | template<typename T>␊ |
187 | struct binder_only␊ |
188 | {␊ |
189 | T * obj;␊ |
190 | boost::function<void(T*)> fun;␊ |
191 | binder_only(boost::function<void(T*)> const & f, T * o)␊ |
192 | : obj(o), fun(f)␊ |
193 | {}␊ |
194 | void operator()()␊ |
195 | {␊ |
196 | fun(obj);␊ |
197 | }␊ |
198 | };␊ |
199 | ␊ |
200 | // Options that need to be attached to some other object␊ |
201 | // in order for set and reset to be meaningful.␊ |
202 | template<typename T>␊ |
203 | struct option␊ |
204 | {␊ |
205 | std::string description;␊ |
206 | std::string names;␊ |
207 | bool has_arg;␊ |
208 | boost::function<void (T*, std::string)> setter;␊ |
209 | boost::function<void (T*)> resetter;␊ |
210 | ␊ |
211 | option(std::string const & name,␊ |
212 | ␉ std::string const & desc,␊ |
213 | ␉ bool arg,␊ |
214 | ␉ void(T::*set)(std::string),␊ |
215 | ␉ void(T::*reset)())␊ |
216 | {␊ |
217 | I(!name.empty() || !desc.empty());␊ |
218 | description = desc;␊ |
219 | names = name;␊ |
220 | has_arg = arg;␊ |
221 | setter = set;␊ |
222 | resetter = reset;␊ |
223 | }␊ |
224 | ␊ |
225 | concrete_option instantiate(T * obj) const␊ |
226 | {␊ |
227 | concrete_option out;␊ |
228 | out.description = description;␊ |
229 | splitname(names, out.longname, out.shortname);␊ |
230 | out.has_arg = has_arg;␊ |
231 | ␊ |
232 | if (setter)␊ |
233 | ␉out.setter = std::bind1st(setter, obj);␊ |
234 | if (resetter)␊ |
235 | ␉out.resetter = binder_only<T>(resetter, obj);␊ |
236 | return out;␊ |
237 | }␊ |
238 | ␊ |
239 | bool operator<(option const & other) const␊ |
240 | {␊ |
241 | if (names != other.names)␊ |
242 | return names < other.names;␊ |
243 | return description < other.description;␊ |
244 | }␊ |
245 | };␊ |
246 | ␊ |
247 | // A group of unattached options, which can be given an object␊ |
248 | // to attach themselves to.␊ |
249 | template<typename T>␊ |
250 | struct option_set␊ |
251 | {␊ |
252 | std::set<option<T> > options;␊ |
253 | option_set(){}␊ |
254 | option_set(option_set<T> const & other)␊ |
255 | : options(other.options)␊ |
256 | {}␊ |
257 | option_set(option<T> const & opt)␊ |
258 | {␊ |
259 | options.insert(opt);␊ |
260 | }␊ |
261 | ␊ |
262 | option_set(std::string const & name,␊ |
263 | ␉ std::string const & desc,␊ |
264 | ␉ bool arg,␊ |
265 | ␉ void(T::*set)(std::string),␊ |
266 | ␉ void(T::*reset)())␊ |
267 | {␊ |
268 | options.insert(option<T>(name, desc, arg, set, reset));␊ |
269 | }␊ |
270 | concrete_option_set instantiate(T * obj) const␊ |
271 | {␊ |
272 | std::set<concrete_option> out;␊ |
273 | for (typename std::set<option<T> >::const_iterator i = options.begin();␊ |
274 | ␉ i != options.end(); ++i)␊ |
275 | ␉out.insert(i->instantiate(obj));␊ |
276 | return out;␊ |
277 | }␊ |
278 | option_set<T> operator | (option_set<T> const & other) const␊ |
279 | {␊ |
280 | option_set<T> combined;␊ |
281 | std::set_union(options.begin(), options.end(),␊ |
282 | ␉␉ other.options.begin(), other.options.end(),␊ |
283 | ␉␉ std::inserter(combined.options, combined.options.begin()));␊ |
284 | return combined;␊ |
285 | }␊ |
286 | option_set<T> operator - (option_set<T> const & other) const␊ |
287 | {␊ |
288 | option_set<T> combined;␊ |
289 | std::set_difference(options.begin(), options.end(),␊ |
290 | other.options.begin(), other.options.end(),␊ |
291 | std::inserter(combined.options,␊ |
292 | combined.options.begin()));␊ |
293 | return combined;␊ |
294 | }␊ |
295 | bool empty() const {return options.empty();}␊ |
296 | };␊ |
297 | template<typename T>␊ |
298 | option_set<T>␊ |
299 | operator | (option<T> const & a, option<T> const & b)␊ |
300 | {␊ |
301 | return option_set<T>(a) | b;␊ |
302 | }␊ |
303 | ␊ |
304 | }␊ |
305 | ␊ |
306 | ␊ |
307 | #endif␊ |
308 | ␊ |
309 | // Local Variables:␊ |
310 | // mode: C++␊ |
311 | // fill-column: 76␊ |
312 | // c-file-style: "gnu"␊ |
313 | // indent-tabs-mode: nil␊ |
314 | // End:␊ |
315 | // vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:␊ |