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