option.hh

Go to the documentation of this file.
00001 //
00002 // $Id: option.hh,v 1.11 2004/01/13 18:08:42 cholm Exp $ 
00003 //  
00004 //  optionmm::option
00005 //  Copyright (C) 2002 Christian Holm Christensen <cholm@nbi.dk> 
00006 //
00007 //  This library is free software; you can redistribute it and/or 
00008 //  modify it under the terms of the GNU Lesser General Public License 
00009 //  as published by the Free Software Foundation; either version 2.1 
00010 //  of the License, or (at your option) any later version. 
00011 //
00012 //  This library is distributed in the hope that it will be useful, 
00013 //  but WITHOUT ANY WARRANTY; without even the implied warranty of 
00014 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
00015 //  Lesser General Public License for more details. 
00016 // 
00017 //  You should have received a copy of the GNU Lesser General Public 
00018 //  License along with this library; if not, write to the Free 
00019 //  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
00020 //  02111-1307 USA 
00021 //
00022 #ifndef OPTIONMM_option
00023 #define OPTIONMM_option
00024 
00025 #ifndef __TYPEINFO__
00026 #include <typeinfo>
00027 #endif
00028 #ifndef __VECTOR__
00029 #include <vector>
00030 #endif
00031 #ifndef __STRING__
00032 #include <string>
00033 #endif
00034 #ifndef __IOSTREAM__
00035 #include <iostream>
00036 #endif
00037 #ifndef __IOMANIP__
00038 #include <iomanip>
00039 #endif
00040 #ifndef __SSTREAM__
00041 #include <sstream>
00042 #endif
00043 #ifndef __STDEXCEPT__
00044 #include <stdexcept>
00045 #endif
00046 
00047 /** @file   option.hh
00048     @author Christian Holm
00049     @date   Sat Dec 28 19:10:36 2002
00050     @brief  Command line option  */
00051 
00052 namespace optionmm
00053 {
00054   //====================================================================
00055   /** @class option_base option.hh <optionmm/option.hh>
00056       @brief Base class for options. 
00057 
00058       This is needed to use the @c std::vector container in the class
00059       command_line below. */
00060   class  option_base 
00061   {
00062   public:
00063     typedef std::vector<int> position_list;
00064   protected:
00065     /// Short name of the option 
00066     char _short_name;
00067     /// Long name of the option 
00068     std::string _long_name;
00069     /// The help string 
00070     std::string _help_string;
00071     /// List of positions 
00072     position_list _positions;
00073   public:
00074     enum {
00075       cannot_handle,
00076       can_handle,
00077       bad_argument, 
00078       missing_argument
00079     };
00080     /** CTOR
00081         @param s The short name (possibly '\\0')
00082         @param l The long name (possibly "");
00083         @param h The help string. */
00084     option_base(char s, 
00085                  const std::string& l, 
00086                  const std::string& h);
00087     /// DTOR 
00088     virtual ~option_base() {}
00089     /** Get the short name 
00090         @return  the short name */
00091     char short_name() const { return _short_name; }
00092     /** Get the long name 
00093         @return  the long name */
00094     const std::string& long_name() const { return _long_name; }
00095     /** Get the help string 
00096         @return the help string  */
00097     const std::string& help_string() const { return _help_string; }
00098     /** Test whether we need an argument 
00099         @return whether we need an argument */
00100     virtual bool need_argument() const { return true; }
00101     /** Test if this option can have many values 
00102         @return  true if this option can have many values */
00103     virtual bool many_values() const { return true; }
00104     /** Set one value 
00105         @param arg The argument 
00106         @param pos Where the argument occured on the command line 
00107         @return  positive if OK, negative on errors */
00108     virtual int push_arg(const char* arg, int pos) = 0;
00109     /** Toggle the option.
00110         @param pos Where the option was set on the command line
00111         @return  positive if OK, negative on errors */
00112     virtual int push_arg(int pos) = 0;
00113     /** Get the position of the @a i'th argument 
00114         @param i Which argument to get the position off  
00115         @return Position of the @a i'th arument, or negative if @a is
00116         out of range   */
00117     virtual int position(int i=0) const;
00118     /** Print this option help line to a stream 
00119         @param ll Width for the option names
00120         @param o The stream to write to */
00121     virtual void print(int ll, std::ostream& o=std::cout) const = 0;
00122     /** Process this as a long option. 
00123         @param arg is the option with leading "--" 
00124         @param pos The position of the option on the command line.
00125         @return positive on success, 0 on if no match, negative on failure. */
00126     virtual int handle(const std::string& arg, int pos);
00127     /** Handle this option as a short. 
00128         @param opt The option 
00129         @param arg The possible argument. 
00130         @param pos The position of the option on the command line.
00131         @return positive on success, 0 on if no match, negative on failure. */
00132     virtual int handle(char*& opt, char*& arg, int pos);
00133   };
00134 
00135   //__________________________________________________________________
00136   inline 
00137   option_base::option_base(char s, 
00138                              const std::string& l, 
00139                              const std::string& h)
00140     : _short_name(s), 
00141       _long_name(l),
00142       _help_string(h)
00143   {}
00144 
00145   //__________________________________________________________________
00146   inline 
00147   int
00148   option_base::position(int i) const
00149   { 
00150     if (i < 0 || i >= _positions.size()) return 0;
00151     if (!many_values()) return _positions[0]; 
00152     return _positions[i];
00153   }
00154 
00155   //__________________________________________________________________
00156   inline 
00157   int
00158   option_base::handle(const std::string& arg, int pos)
00159   {
00160     if (_long_name.empty() || 
00161 #if defined(__GNUC__) && __GNUC__ <= 2
00162         arg.compare(_long_name, size_t(2), _long_name.size())
00163 #else 
00164         arg.compare(size_t(2), _long_name.size(), _long_name)
00165 #endif
00166         ) return cannot_handle;
00167     if (!need_argument()) return push_arg(pos);
00168 
00169     std::string::size_type eq = arg.find_last_of('=');
00170     if (eq == std::string::npos) return missing_argument;
00171     
00172     std::string value(arg);
00173     value.erase(0, eq+1);
00174     return push_arg(value.c_str(), pos);
00175   }
00176   
00177   //__________________________________________________________________
00178   inline 
00179   int
00180   option_base::handle(char*& opt, char*& arg, int pos)
00181   {
00182     if (_short_name == '\0' || opt[0] != _short_name) return cannot_handle;
00183     if (!need_argument())                             return push_arg(pos);
00184 
00185     int ret = can_handle;
00186     if (opt[1] != '\0') {
00187       ret = push_arg(&(opt[1]), pos);
00188       if (ret > cannot_handle) opt[1] = '\0';
00189     }
00190     else if (arg) {
00191       ret = push_arg(arg, pos);
00192       // Flag argument as used. 
00193       if (ret > cannot_handle) arg = 0;
00194     }
00195     else 
00196       ret =  missing_argument;
00197     
00198     return ret;
00199   }
00200   
00201   //==================================================================
00202   /** @class option_trait optionmm/option.hh <optionmm/option.hh>
00203       @brief Trait to help do conversions. 
00204   */
00205   template <typename Type>
00206   struct option_trait 
00207   {
00208     /** Type of the trait */
00209     typedef Type                             value_type;
00210     /** Container used by the option */
00211     typedef std::vector<value_type>          list_type;
00212     /** Reference type used by the container */
00213     typedef typename list_type::reference    reference;
00214     
00215     /** Convert a string to the value type 
00216         @param arg The string to convert
00217         @param val The resulting value 
00218         @return  true on success, false otherwise. */
00219     static bool convert(const char* arg, value_type& val)
00220     {
00221       std::stringstream str(arg);
00222       str.unsetf(std::ios::skipws);
00223       str >> val;
00224       if (str.bad() || str.fail()) return false;
00225       std::string t;
00226       str >> t;
00227       if (!t.empty()) return false;
00228       return true;
00229     }
00230     /** Toggle a value.
00231         @param val The value to toggle
00232         @return  true on success */
00233     static bool convert(reference val) 
00234     {
00235       value_type tmp = !val;
00236       val = tmp;
00237       return true;
00238     }
00239   };
00240 
00241   //================================================================== 
00242   template <> 
00243   struct option_trait<std::string> 
00244   {
00245     typedef std::string value_type;
00246     typedef std::vector<value_type> list_type;
00247     typedef list_type::reference    reference;
00248     
00249     static bool convert(const char* arg, value_type& val)
00250     {
00251       val = arg;
00252       return true;
00253     }
00254     static bool convert(reference val) 
00255     {
00256       return true;
00257     }
00258   };
00259  
00260   //====================================================================
00261   /** @class basic_option option.hh <optionmm/option.hh>
00262       @brief Command line option class.
00263 
00264       The argument @c Type is the type of the value, @c argument
00265       specifies whether this option needs an argument, and 
00266       @c multivalue says whether the option can take multiple values. */
00267   template <typename Type, 
00268             bool argument=true, 
00269             bool multivalue=true, 
00270             typename Trait=option_trait<Type> >
00271   class basic_option : public option_base
00272   {
00273   public:
00274     typedef Type value_type;    /** The value type */
00275     typedef std::vector<value_type> value_list; /** Type of value container */
00276     typedef Trait trait_type;
00277   protected:
00278     value_list _values;         /** A list of the values */
00279     value_type _default;        /** Default value */
00280   public:
00281     /** Constructor.
00282         @param s The short name (possibly '\\0')
00283         @param l The long name (possibly "");
00284         @param h The help string. 
00285         @param d The default value. */    
00286     basic_option(char s, 
00287                  const std::string& l,
00288                  const std::string& h, 
00289                  value_type d);
00290     /** Destructor. */
00291     virtual ~basic_option() {}
00292 
00293     /** Whether this option need an argument */
00294     bool need_argument() const { return argument; }  
00295     /** Whether this optin can take many values */ 
00296     bool many_values() const { return multivalue; }  
00297     /** Number of values of this option */
00298     int size() const { return  (_values.size() == 0 ? 1 : _values.size()); }
00299 
00300     /** Get value
00301         @param i The value number to get. 
00302         @return the @p i value, or if that is out of bounds, the first
00303         value. */ 
00304     const value_type& value(int i=0) const;
00305     /** Get all values */ 
00306     const value_list& values() const { return _values; }
00307 
00308     /** Add a value from a string.  This is used by the command_line
00309         class to set the values of the options, in case need_argument 
00310         tests true. 
00311         @param pos The position on the command line the option was
00312         given at.
00313         @param arg The value added. */
00314     int push_arg(const char* arg, int pos);
00315     /** Add a value.  This is used by the command_line
00316         class to set the values of the options, in case need_argument 
00317         tests false. 
00318         @param pos The position on the command line the option was
00319         given at. */
00320     int push_arg(int pos);
00321 
00322     /** Print this option help line to a stream 
00323         @param ll Width for the option names
00324         @param o The stream to write to */
00325     virtual void print(int ll, std::ostream& o=std::cout) const;
00326   };
00327   //____________________________________________________________________
00328   template<typename Type, bool arg, bool multi, typename Trait>
00329   inline 
00330   basic_option<Type,arg,multi,Trait>::basic_option(char s, 
00331                                                    const std::string& l, 
00332                                                    const std::string& h, 
00333                                                    value_type d) 
00334     : option_base(s, l, h), _default(d) 
00335   {}
00336   
00337   //____________________________________________________________________
00338   template<typename Type, bool arg, bool multi, typename Trait> 
00339   inline const Type& 
00340   basic_option<Type,arg,multi,Trait>::value(int i) const
00341   { 
00342     if (i >= _values.size() || i < 0) return _default; 
00343     const value_type& ret = _values[i];
00344     return ret;
00345   }
00346 
00347   //____________________________________________________________________
00348   template<typename Type, bool arg, bool multi, typename Trait> 
00349   inline 
00350   int
00351   basic_option<Type,arg,multi,Trait>::push_arg(const char* a, int pos) 
00352   { 
00353     value_type val;
00354     if (!trait_type::convert(a, val)) return bad_argument;
00355     
00356     if (!many_values()) {
00357       if (_values.size() == 0) {
00358         _values.push_back(val);
00359         _positions.push_back(pos);
00360       }
00361       else {
00362         _values[0]    = val;
00363         _positions[0] = pos;
00364       }
00365     } else {
00366       _values.push_back(val);
00367       _positions.push_back(pos);
00368     }
00369     return can_handle;
00370   }
00371 
00372   //____________________________________________________________________
00373   template<typename Type, bool arg, bool multi, typename Trait> 
00374   inline 
00375   int
00376   basic_option<Type,arg,multi,Trait>::push_arg(int pos) 
00377   { 
00378     if (many_values()) {
00379       _values.push_back(_default);
00380       _positions.push_back(pos);
00381     }
00382     else {
00383       if (_values.size() == 0) {
00384         _values.push_back(_default);
00385         _positions.push_back(pos);
00386       }
00387       else 
00388         _positions[0] = pos;
00389       trait_type::convert(_values[0]); // = !_values[0];
00390     }
00391     return can_handle;
00392   }
00393 
00394   //__________________________________________________________________
00395   template<typename Type, bool arg, bool multi, typename Trait> 
00396   inline 
00397   void
00398   basic_option<Type,arg,multi,Trait>::print(int ll, std::ostream& o) const
00399   {
00400     if (ll <= 0) ll=16;
00401     if (_short_name != '\0') o << "-" << _short_name <<", ";
00402     else                     o << "    ";
00403     if (!_long_name.empty()) {
00404       o << "--" << _long_name;
00405       if (need_argument()) o << "=VALUE";
00406       else                 o << "      ";
00407       for (int i = _long_name.size(); i < ll; i++) o << " ";
00408     }
00409     else {
00410       o << std::setw(ll+2) << " ";
00411       if (need_argument()) o << " VALUE";
00412       else                 o << "      ";
00413     }
00414     o << "\t" << _help_string; // << " (default: " << _default << ")";
00415     if (many_values()) o << " (*)";
00416   }  
00417 }
00418 
00419 #endif
00420 //____________________________________________________________________
00421 //
00422 // EOF
00423 //
00424 
Top of page Last update Tue Jan 13 19:10:34 2004
Christian Holm
Created by DoxyGen 1.3.4