Home Hierarchy Members Alphabetical Related Pages

argstream.h

Go to the documentation of this file.
00001 #ifndef XDKWRL_ARGSTREAM_H
00002 #define XDKWRL_ARGSTREAM_H
00003 
00004 #include <string>
00005 #include <list>
00006 #include <deque>
00007 #include <map>
00008 #include <stdexcept>
00009 #include <sstream>
00010 #include <iostream> 
00011 
00012 class argstream;
00013 //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00014 // Interface of ValueHolder<T>
00015 //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00016 template<class T>
00017 class ValueHolder
00018 {
00019 public:
00020   ValueHolder(const char s,
00021               const char* l,
00022               T& b,
00023               const char* desc,
00024               bool mandatory);
00025   friend argstream& operator>><>(argstream& s,const ValueHolder<T>& v);
00026   std::string name() const;
00027   std::string description() const;
00028 private:
00029   std::string shortName_;
00030   std::string longName_;
00031   T*          value_;
00032   T           initialValue_;
00033   std::string description_;  
00034   bool        mandatory_;
00035 };
00036 template <class T>
00037 inline ValueHolder<T>
00038 parameter(const char s,
00039           const char* l,
00040           T& b,
00041           const char* desc="",
00042           bool mandatory = true)
00043 {
00044   return ValueHolder<T>(s,l,b,desc,mandatory);
00045 }
00046 template <class T>
00047 inline ValueHolder<T>
00048 parameter(const char* l,
00049           T& b,
00050           const char* desc="",
00051           bool mandatory = true)
00052 {
00053   return ValueHolder<T>(0,l,b,desc,mandatory);
00054 }
00055 //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00056 // Interface of OptionHolder
00057 //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00058 class OptionHolder
00059 {
00060 public:
00061   OptionHolder(const char s,
00062                const char* l,
00063                bool& b,
00064                const char* desc);
00065   friend argstream& operator>>(argstream& s,const OptionHolder& v);
00066   std::string name() const;
00067   std::string description() const;
00068 protected:
00069   OptionHolder(const char s,
00070                const char* l,
00071                const char* desc);  
00072   friend OptionHolder help(const char s='h',
00073                            const char* l="help",
00074                            const char* desc="Display this help");
00075 private:
00076   std::string shortName_;
00077   std::string longName_;
00078   bool*       value_;
00079   std::string description_;  
00080 };
00081 inline OptionHolder
00082 option(const char s,
00083        const char* l,
00084        bool& b,
00085        const char* desc="")
00086 {
00087   return OptionHolder(s,l,b,desc);
00088 }
00089 inline OptionHolder
00090 option(const char* l,
00091        bool& b,
00092        const char* desc="")
00093 {
00094   return OptionHolder(0,l,b,desc);
00095 }
00096 inline OptionHolder
00097 help(const char s,
00098      const char* l,
00099      const char* desc)
00100 {
00101   return OptionHolder(s,l,desc);
00102 }
00103 //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00104 // Interface of ValuesHolder
00105 //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00106 template<class T,class O>
00107 class ValuesHolder
00108 {
00109 public:
00110   ValuesHolder(const O& o,
00111                const char* desc,
00112                int len);
00113   friend argstream& operator>><>(argstream& s,const ValuesHolder<T,O>& v);
00114   std::string name() const;
00115   std::string description() const;
00116   typedef T value_type;
00117 private:
00118   mutable O   value_;
00119   std::string description_;  
00120   int         len_;
00121   char        letter_;
00122 };
00123 template<class T,class O>
00124 inline ValuesHolder<T,O>
00125 values(const O& o,
00126        const char* desc="",
00127        int len=-1)
00128 {
00129   return ValuesHolder<T,O>(o,desc,len);
00130 }
00131 //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00132 // Interface of argstream
00133 //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00134 class argstream
00135 {
00136 public:
00137   argstream(int argc,char** argv,bool debug=false);
00138   template<class T>
00139   friend argstream& operator>>(argstream& s,const ValueHolder<T>& v);
00140   friend argstream& operator>>(argstream& s,const OptionHolder& v);
00141   template<class T,class O>
00142   friend argstream& operator>>(argstream& s,const ValuesHolder<T,O>& v);
00143 
00144   bool helpRequested() const;
00145   bool isOk() const;
00146   std::string errorLog() const;
00147   std::string usage() const;
00148   void defaultErrorHandling(bool ignoreUnused=false) const;
00149   static inline char uniqueLetter();
00150 private:
00151   typedef std::list<std::string>::iterator value_iterator;
00152   typedef std::pair<std::string,std::string> help_entry;
00153   bool                                 debug_;
00154   std::string                          progName_;
00155   std::map<std::string,value_iterator> options_;
00156   std::list<std::string>               values_;
00157   bool                                 minusActive_;
00158   bool                                 isOk_;
00159   std::deque<help_entry>               argHelps_;
00160   std::string                          cmdLine_;
00161   std::deque<std::string>              errors_;
00162   bool                                 helpRequested_;
00163 };
00164 //************************************************************
00165 // Implementation of ValueHolder<T>
00166 //************************************************************
00167 template<class T>
00168 ValueHolder<T>::ValueHolder(const char s,
00169                             const char* l,
00170                             T& v,
00171                             const char* desc,
00172                             bool mandatory)
00173   :  shortName_(1,s),
00174      longName_(l),
00175      value_(&v),
00176      initialValue_(v),
00177      description_(desc),
00178      mandatory_(mandatory)
00179 {
00180 }
00181 template<class T>
00182 std::string
00183 ValueHolder<T>::name() const
00184 {
00185   std::ostringstream  os;
00186   if (shortName_[0] != 0)
00187   {
00188     os<<'-'<<shortName_<<'/';
00189   }
00190   os<<"--"<<longName_;
00191   return os.str();
00192 }
00193 template<class T>
00194 std::string
00195 ValueHolder<T>::description() const
00196 {
00197   std::ostringstream  os;  
00198   os<<description_;
00199   if (mandatory_)
00200   {
00201     os<<"(mandatory)";
00202   }
00203   else
00204   {
00205     os<<"(default="<<initialValue_<<")";
00206   }
00207   return os.str();
00208 }
00209 //************************************************************
00210 // Implementation of OptionHolder
00211 //************************************************************
00212 OptionHolder::OptionHolder(const char s,
00213                            const char* l,
00214                            bool& b,
00215                            const char* desc)
00216   : shortName_(1,s),
00217      longName_(l),
00218      value_(&b),
00219      description_(desc)
00220 {
00221 }
00222 OptionHolder::OptionHolder(const char s,
00223                            const char* l,
00224                            const char* desc)
00225   : shortName_(1,s),
00226      longName_(l),
00227      value_(NULL),
00228      description_(desc)
00229 {
00230 }
00231 std::string
00232 OptionHolder::name() const
00233 {
00234   std::ostringstream  os;
00235   if (shortName_[0] != 0)
00236   {
00237     os<<'-'<<shortName_<<'/';
00238   }
00239   os<<"--"<<longName_;
00240   return os.str();
00241 }
00242 std::string
00243 OptionHolder::description() const
00244 {
00245   return description_;
00246 }
00247 //************************************************************
00248 // Implementation of ValuesHolder<T,O>
00249 //************************************************************
00250 template<class T,class O>
00251 ValuesHolder<T,O>::ValuesHolder(const O& o,
00252                                 const char* desc,
00253                                 int len)
00254   : value_(o),
00255     description_(desc),
00256     len_(len)
00257 {
00258   letter_ = argstream::uniqueLetter();
00259 }
00260 template <class T,class O>
00261 std::string
00262 ValuesHolder<T,O>::name() const
00263 {
00264   std::ostringstream  os;
00265   os<<letter_<<"i";
00266   return os.str();
00267 }
00268 template <class T,class O>
00269 std::string
00270 ValuesHolder<T,O>::description() const
00271 {
00272   return description_;
00273 }
00274 //************************************************************
00275 // Implementation of argstream
00276 //************************************************************
00277 inline
00278 argstream::argstream(int argc,char** argv,bool debug)
00279   : debug_(debug),
00280     progName_(argv[0]),
00281     minusActive_(true),
00282     isOk_(true)
00283 {
00284   // Run thru all arguments.
00285   // * it has -- in front : it is a long name option, if remainder is empty,
00286   //                        it is an error
00287   // * it has - in front  : it is a sequence of short name options, if
00288   //                        remainder is empty, deactivates option (- will
00289   //                        now be considered a char).
00290   // * if any other char, or if option was deactivated
00291   //                      : it is a value. Values are split in parameters
00292   //                      (immediately follow an option) and pure values.
00293   // Each time a value is parsed, if the previously parsed argument was an
00294   // option, then the option is linked to the value in case of it is a
00295   // option with parameter.  The subtle point is that when several options
00296   // are given with short names (ex: -abc equivalent to -a -b -c), the last
00297   // parsed option is -c).
00298   // Since we use map for option, any successive call overides the previous
00299   // one: foo -a -b -a hello is equivalent to foo -b -a hello
00300   // For values it is not true since we might have several times the same
00301   // value. 
00302   value_iterator* lastOption = NULL;
00303   for (char** a = argv,**astop=a+argc;++a!=astop;)
00304   {
00305     std::string s(*a);
00306     if (minusActive_ && s[0] == '-')
00307     {
00308       if (s.size() > 1 && s[1] == '-')
00309       {
00310         if (s.size() == 2)
00311         {
00312           minusActive_ = false;
00313           continue;
00314         }
00315         lastOption = &(options_[s.substr(2)] = values_.end());
00316       }
00317       else 
00318       {
00319         if (s.size() > 1)
00320         {
00321           // Parse all chars, if it is a minus we have an error
00322           for (std::string::const_iterator cter = s.begin();
00323                ++cter != s.end();)
00324           {
00325             if (*cter == '-')
00326             {
00327               isOk_ = false;
00328               std::ostringstream os;
00329               os<<"- in the middle of a switch "<<a;
00330               errors_.push_back(os.str());
00331               break;
00332             }
00333             lastOption = &(options_[std::string(1,*cter)] = values_.end());
00334           }
00335         }
00336         else
00337         {
00338           isOk_ = false;
00339           errors_.push_back("Invalid argument -");
00340           break;
00341         }
00342       }
00343     }
00344     else
00345     {
00346       values_.push_back(s);
00347       if (lastOption != NULL)
00348       {
00349         *lastOption = --values_.end();
00350       }
00351       lastOption = NULL;
00352     }
00353   }
00354   if (debug_)
00355   {
00356     for (std::map<std::string,value_iterator>::const_iterator
00357            iter = options_.begin();iter != options_.end();++iter)
00358     {
00359       std::cout<<"DEBUG: option "<<iter->first;
00360       if (iter->second != values_.end())
00361       {
00362         std::cout<<" -> "<<*(iter->second);
00363       }
00364       std::cout<<std::endl;
00365     }
00366     for (std::list<std::string>::const_iterator
00367            iter = values_.begin();iter != values_.end();++iter)
00368     {
00369       std::cout<<"DEBUG: value  "<<*iter<<std::endl;
00370     }
00371   }
00372 }
00373 bool
00374 argstream::isOk() const
00375 {
00376   return isOk_;
00377 }
00378 bool
00379 argstream::helpRequested() const
00380 {
00381   return helpRequested_;
00382 }
00383 std::string
00384 argstream::usage() const
00385 {
00386   std::ostringstream os;
00387   os<<"usage: "<<progName_<<cmdLine_<<'\n';
00388   unsigned int lmax = 0;
00389   for (std::deque<help_entry>::const_iterator
00390          iter = argHelps_.begin();iter != argHelps_.end();++iter)
00391   {
00392     if (lmax<iter->first.size()) lmax = iter->first.size();
00393   }  
00394   for (std::deque<help_entry>::const_iterator
00395          iter = argHelps_.begin();iter != argHelps_.end();++iter)
00396   {
00397     os<<'\t'<<iter->first<<std::string(lmax-iter->first.size(),' ')
00398       <<" : "<<iter->second<<'\n';
00399   }
00400   return os.str();
00401 }
00402 std::string
00403 argstream::errorLog() const
00404 {
00405   std::string s;
00406   for(std::deque<std::string>::const_iterator iter = errors_.begin();
00407       iter != errors_.end();++iter)
00408   {
00409     s += *iter;
00410     s += '\n';
00411   }
00412   return s;
00413 }
00414 inline char
00415 argstream::uniqueLetter()
00416 {
00417   static unsigned int c = 'a';
00418   return c++;
00419 }
00420 template<class T>
00421 argstream&
00422 operator>>(argstream& s,const ValueHolder<T>& v)
00423 {
00424   // Search in the options if there is any such option defined either with a
00425   // short name or a long name. If both are found, only the last one is
00426   // used.
00427   if (s.debug_)
00428   {
00429     std::cout<<"DEBUG: searching "<<v.shortName_<<" "<<v.longName_<<std::endl;
00430   }
00431   s.argHelps_.push_back(argstream::help_entry(v.name(),v.description()));
00432   if (v.mandatory_)
00433   {
00434     if (v.shortName_[0] != 0)
00435     {
00436       s.cmdLine_ += " -";
00437       s.cmdLine_ += v.shortName_;
00438     }
00439     else
00440     {
00441       s.cmdLine_ += " --";
00442       s.cmdLine_ += v.longName_;
00443     }
00444     s.cmdLine_ += " value";
00445     
00446   }
00447   else
00448   {
00449     if (v.shortName_[0] != 0)
00450     {
00451       s.cmdLine_ += " [-";
00452       s.cmdLine_ += v.shortName_;
00453     }
00454     else
00455     {
00456       s.cmdLine_ += " [--";
00457       s.cmdLine_ += v.longName_;
00458     }  
00459     s.cmdLine_ += " value]";
00460     
00461   }
00462   std::map<std::string,argstream::value_iterator>::iterator iter =
00463     s.options_.find(v.shortName_);
00464   if (iter == s.options_.end())
00465   {
00466     iter = s.options_.find(v.longName_);
00467   }
00468   if (iter != s.options_.end())
00469   {
00470     // If we find counterpart for value holder on command line, either it
00471     // has an associated value in which case we assign it, or it has not, in
00472     // which case we have an error.
00473     if (iter->second != s.values_.end())
00474     {
00475       if (s.debug_)
00476       {
00477         std::cout<<"DEBUG: found value "<<*(iter->second)<<std::endl;
00478       }
00479       std::istringstream is(*(iter->second));
00480       is>>(*(v.value_));
00481       // The option and its associated value are removed, the subtle thing
00482       // is that someother options might have this associated value too,
00483       // which we must invalidate.
00484       s.values_.erase(iter->second);      
00485       for (std::map<std::string,argstream::value_iterator>::iterator
00486              jter = s.options_.begin();jter != s.options_.end();++jter)
00487       {
00488         if (jter->second == iter->second)
00489         {
00490           jter->second = s.values_.end();
00491         }
00492       }
00493       s.options_.erase(iter);
00494     }
00495     else
00496     {
00497       s.isOk_ = false;
00498       std::ostringstream  os;
00499       os<<"No value following switch "<<iter->first
00500         <<" on command line";
00501       s.errors_.push_back(os.str());
00502     }
00503   }
00504   else
00505   {
00506     if (v.mandatory_)
00507     {
00508       s.isOk_ = false;
00509       std::ostringstream  os;
00510       os<<"Mandatory parameter ";
00511       if (v.shortName_[0] != 0)
00512       {
00513         os<<'-'<<v.shortName_<<'/';
00514       }
00515       os<<"--"<<v.longName_<<" missing";
00516       s.errors_.push_back(os.str());
00517     }
00518   }
00519   return s;
00520 }
00521 argstream&
00522 operator>>(argstream& s,const OptionHolder& v)
00523 {
00524   // Search in the options if there is any such option defined either with a
00525   // short name or a long name. If both are found, only the last one is
00526   // used.
00527   if (s.debug_)
00528   {
00529     std::cout<<"DEBUG: searching "<<v.shortName_<<" "<<v.longName_<<std::endl;
00530   }
00531   s.argHelps_.push_back(argstream::help_entry(v.name(),v.description()));
00532   {
00533     std::string c;
00534     if (v.shortName_[0] != 0)
00535     {
00536       c += " [-";
00537       c += v.shortName_;
00538     }
00539     else
00540     {
00541       c += " [--";
00542       c += v.longName_;
00543     }      
00544     c += "]";
00545     s.cmdLine_ = c+s.cmdLine_;
00546   }
00547   std::map<std::string,argstream::value_iterator>::iterator iter =
00548     s.options_.find(v.shortName_);
00549   if (iter == s.options_.end())
00550   {
00551     iter = s.options_.find(v.longName_);
00552   }
00553   if (iter != s.options_.end())
00554   {
00555     // If we find counterpart for value holder on command line then the
00556     // option is true and if an associated value was found, it is ignored
00557     if (v.value_ != NULL)
00558     {
00559       *(v.value_) = true;
00560     }
00561     else
00562     {
00563       s.helpRequested_ = true;
00564     }
00565     // The option only is removed
00566     s.options_.erase(iter);
00567   }
00568   else
00569   {
00570     if (v.value_ != NULL)
00571     {
00572       *(v.value_) = false;
00573     }
00574     else
00575     {
00576       s.helpRequested_ = false;
00577     }
00578   }
00579   return s;
00580 }
00581 template<class T,class O>
00582 argstream&
00583 operator>>(argstream& s,const ValuesHolder<T,O>& v)
00584 {
00585   s.argHelps_.push_back(argstream::help_entry(v.name(),v.description()));
00586   {
00587     std::ostringstream os;
00588     os<<' '<<v.letter_<<'1';
00589     switch (v.len_)
00590     {
00591     case -1:
00592       os<<"...";
00593       break;
00594     case 1:
00595       break;
00596     default:
00597       os<<"..."<<v.letter_<<v.len_;
00598       break;
00599     }
00600     s.cmdLine_ += os.str();
00601   }
00602   std::list<std::string>::iterator first = s.values_.begin();
00603   // We add to the iterator as much values as we can, limited to the length
00604   // specified (if different of -1)
00605   int n = v.len_ != -1?v.len_:s.values_.size();
00606   while (first != s.values_.end() && n-->0)
00607   {
00608     std::istringstream is(*first);
00609     T t;
00610     is>>t;
00611     *(v.value_++) = t;
00612     for (std::map<std::string,argstream::value_iterator>::iterator
00613            jter = s.options_.begin();jter != s.options_.end();++jter)
00614     {
00615       if (jter->second == first)
00616       {
00617         jter->second = s.values_.end();
00618       }
00619     }
00620     ++first;
00621   }
00622   // Check if we have enough values
00623   if (n != 0)
00624   {
00625     s.isOk_ = false;
00626     std::ostringstream  os;
00627     os<<"Expecting "<<v.len_<<" values";
00628     s.errors_.push_back(os.str());
00629   }
00630   // Erase the values parsed
00631   s.values_.erase(s.values_.begin(),first);
00632   return s;
00633 }
00634 void
00635 argstream::defaultErrorHandling(bool ignoreUnused) const
00636 {
00637   if (helpRequested_)
00638   {
00639     std::cout<<usage();
00640     exit(1);
00641   }
00642   if (!isOk_)
00643   {
00644     std::cerr<<errorLog();
00645     exit(1);
00646   }
00647   if (!ignoreUnused &&
00648       (!values_.empty() || !options_.empty()))
00649   {
00650     std::cerr<<"Unused arguments"<<std::endl;
00651     exit(1);
00652   }
00653 }
00654 #endif // XDKWRL_ARGSTREAM_H
00655 // TODO: check for multiple unbound values list request
00656 
00657 // Local variables section.
00658 // This is only used by emacs!
00659 // Local Variables:
00660 // ff-search-directories: ("." "../../../src/xdkwrl/tools/")
00661 // End:

Generated on 24 Feb 2005 with doxygen version 1.3.9.1. Valid HTML 4.0! Valid CSS!