OpenADFortTk (basic)
src/lib/support/CmdLineParser.h
Go to the documentation of this file.
00001 // ##########################################################
00002 // # This file is part of OpenADFortTk.                     #
00003 // # The full COPYRIGHT notice can be found in the top      #
00004 // # level directory of the OpenADFortTk source tree.       #
00005 // # For more information visit                             #
00006 // # http://www.mcs.anl.gov/openad                          #
00007 // ##########################################################
00008 
00009 #ifndef CmdLineParser_H 
00010 #define CmdLineParser_H
00011 
00012 #include <iostream>
00013 #include <map>
00014 #include <vector>
00015 #include <string>
00016 
00017 //*************************** User Include Files ****************************
00018 
00019 #include <inttypes.h> /* commonly available, unlike <stdint.h> */
00020 
00021 //*************************** Forward Declarations ***************************
00022 
00023 //****************************************************************************
00024 
00025 //****************************************************************************
00026 // CmdLineParser
00027 //****************************************************************************
00028 
00029 // CmdLineParser: Parses arguments on the command line, argc and argv.
00030 // Provides easy access to both optional arguments (with short or long
00031 // specifiers) and regular arguments.  Provides functionality similar
00032 // to getopt() and GNU's getopt_long(), but in an easier to use -- and
00033 // in the case of getopt_long(), more portable -- package.  In
00034 // addition, the package provides configurable handling of duplicate
00035 // options/arguments and routines to convert string arguments into
00036 // numerical types.
00037 //
00038 // A user creates a NULL-terminated array of option descriptors
00039 // (OptArgDesc) indicating switch names and arguments, if any.  He
00040 // then instantiates a CmdLineParser and parses argc/argv.  Errors are
00041 // delivered with the exception Exception.  The parser object provides
00042 // access to all optional and regular arguments using the interface
00043 // routines belows.
00044 // 
00045 // More details:
00046 // The command line generally has the form (but note qualifications below):
00047 //   <command> [arguments]
00048 //   
00049 //   [arguments] ::= [optional_arg] | [regular_arg]
00050 //   
00051 //   [optional_arg] ::= -f [arg] | -f[arg]
00052 //                    | --foo [arg] | --foo=arg
00053 //
00054 // An element of argv that starts with '-' or '--' (and is not exactly
00055 // '--') signifies an option.  The option switch is the text without
00056 // the initial dashes.  As the above shows, we support a variety of
00057 // option styles.
00058 //
00059 // Features: 
00060 //   - The '--' token forces the end of optional argument scanning and
00061 //     iterprets everything following as a list of regular arguments.
00062 //     This is useful for non-option arguments that begin with dashes.
00063 //   - Unlike getopt() we do not support the deprecated use of the single
00064 //     '-'; this is an error.  
00065 //   - Long argument switches may be abbreviated if the abbreviation 
00066 //     is unique. 
00067 //   - Configurable handling of duplicate options and arguments.
00068 //
00069 // Limitations:
00070 //   - Unlike getopt(), we do not currently support short switch grouping,
00071 //     e.g. using -abc instead of -a -b -c.  [FIXME: we can assume that 
00072 //     only options without arguments are allowed to be grouped.]
00073 //
00074 // Warnings:
00075 //   - Switches that take optional arguments can be confusing.  For
00076 //     example, assume a command 'foo' takes a filename and on option,
00077 //     --debug, which itself takes an optional debug level.  The
00078 //     following commands pose no difficulties:
00079 //       foo myfile
00080 //       foo --debug=3 myfile
00081 //       foo --debug 3 myfile
00082 //       foo myfile --debug
00083 //     However, in the following
00084 //       foo --debug myfile
00085 //     'myfile' is erroneously assumed to be the optional argument to
00086 //     --debug.  While the '--' token solves this, it remains awkward.
00087 //
00088 class CmdLineParser {
00089 public:
00090 
00091   // ---------------------------------------------------------
00092   // Structure used to describe command line options
00093   // ---------------------------------------------------------
00094   
00095   // Describes if an option switch takes an argument
00096   enum OptKind { 
00097     ARG_NULL = 0,
00098     ARG_NONE, // switch does not take argument
00099     ARG_REQ,  // switch must take an argument
00100     ARG_OPT   // switch takes an (optional!) argument
00101   };
00102   
00103   // Describes how to handle duplicate options and option arguments
00104   enum DupOptKind {
00105     DUPOPT_NULL = 0,
00106     DUPOPT_ERR,  // throw an exception for duplicate option or argument
00107     DUPOPT_CLOB, // clobber any previous argument
00108     DUPOPT_CAT   // concat all available arguments using 'dupArgSep'
00109   };
00110   
00111   struct OptArgDesc {
00112     
00113     bool operator==(const OptArgDesc& x) const { 
00114       return (swShort == x.swShort && swLong == x.swLong 
00115               && kind == x.kind && dupKind == x.dupKind
00116               && dupArgSep == x.dupArgSep);
00117     }
00118     bool operator!=(const OptArgDesc& x) const { return !(*this == x); }
00119     
00120     // Data
00121     char swShort;       // 0 if n/a
00122     const char* swLong; // NULL if n/a
00123     OptKind kind;
00124     DupOptKind dupKind;
00125     const char* dupArgSep; // separator for 'DUPARG_CONCAT'
00126   };
00127 
00128   // The NULL terminator (two versions).  The use of the first version
00129   // is preferable, but some older compilers won't support it.
00130   static OptArgDesc OptArgDesc_NULL;
00131 # define CmdLineParser_OptArgDesc_NULL_MACRO \
00132     { 0, NULL, CmdLineParser::ARG_NULL, CmdLineParser::DUPOPT_NULL, NULL }
00133   
00134   
00135   // ---------------------------------------------------------
00136   // Exception thrown when errors are encountered
00137   // ---------------------------------------------------------
00138   
00139   class Exception {
00140   public:
00141     Exception(const char* m) : msg(m) { }
00142     Exception(std::string m) : msg(m) { }
00143     virtual ~Exception () { }
00144 
00145     virtual const std::string& GetMessage() const { return msg; }
00146     virtual void Report(std::ostream& os) const { 
00147       os << "CmdLineParser::Exception: " << msg << std::endl; 
00148     }    
00149     virtual void Report() const { Report(std::cerr); }
00150 
00151   protected: 
00152     std::string msg;
00153   };
00154 
00155   class ParseError : public Exception {
00156   public:
00157     ParseError(const char* m) : Exception(m) { }
00158     ParseError(std::string m) : Exception(m) { }
00159     virtual ~ParseError () { }
00160   };
00161 
00162   class InternalError : public Exception {
00163   public:
00164     InternalError(const char* m) : Exception(m) { }
00165     InternalError(std::string m) : Exception(m) { }
00166     virtual ~InternalError () { }
00167   private:
00168     void Ctor() {
00169       msg = "CmdLineParser internal error (Don't abuse me!): " + msg;
00170     }
00171   };
00172   
00173   // ---------------------------------------------------------
00174 
00175 public:
00176   // ---------------------------------------------------------
00177   // Constructor/Destructor
00178   // ---------------------------------------------------------
00179   CmdLineParser(); 
00180   CmdLineParser(const OptArgDesc* optArgDescs, 
00181                 int argc, const char* const argv[]); 
00182   ~CmdLineParser();
00183 
00184   // -------------------------------------------------------
00185   // Parsing
00186   // -------------------------------------------------------
00187   
00188   // Parse: Given a NULL terminated array of OptArgDesc describing
00189   // command line arguments, parses the argv/argc into switches,
00190   // optional and required arguments.
00191   void
00192   Parse(const OptArgDesc* optArgDescs, 
00193         int argc, const char* const argv[]);
00194   
00195   // -------------------------------------------------------
00196   // Parsed Data: Command
00197   // -------------------------------------------------------
00198 
00199   // GetCmd: The command (will be valid even after a parse error)
00200   const std::string& GetCmd() const;
00201   
00202   // -------------------------------------------------------
00203   // Parsed Data: Optional arguments
00204   // -------------------------------------------------------
00205   
00206   // IsOpt: (IsOption) Given a short or long switch, returns whether
00207   // the switch has been seen.
00208   bool IsOpt(const char swShort) const;
00209   bool IsOpt(const char* swLong) const;
00210   bool IsOpt(const std::string& sw) const;
00211 
00212   // IsOptArg: (IsOptionArgument) Given a short or long switch,
00213   // returns whether an argument is associated with it.  Designed for
00214   // switches that optionally take arguments.
00215   bool IsOptArg(const char swShort) const;
00216   bool IsOptArg(const char* swLong) const;
00217   bool IsOptArg(const std::string& sw) const;  
00218   
00219   // GetOptArg: (GetOptionArgument) Given a short or long switch, get
00220   // the argument associated with it.  Assumes user has verified that
00221   // an argument *exists*.
00222   const std::string& GetOptArg(const char swShort) const;
00223   const std::string& GetOptArg(const char* swLong) const;
00224   const std::string& GetOptArg(const std::string& sw) const;
00225 
00226   // -------------------------------------------------------
00227   // Parsed Data: Arguments
00228   // -------------------------------------------------------
00229   unsigned int GetNumArgs() const;
00230   const std::string& GetArg(unsigned int i) const;
00231 
00232   // -------------------------------------------------------
00233   // Convert strings into other formats
00234   // -------------------------------------------------------
00235   // The input should be non-empty
00236   static long     ToLong(const std::string& str);
00237   static uint64_t ToUInt64(const std::string& str);
00238   static double   ToDbl(const std::string& str);
00239   
00240   // -------------------------------------------------------
00241   // Misc
00242   // -------------------------------------------------------
00243   void Dump(std::ostream& os = std::cerr) const;
00244   void DDump() const;
00245   
00246 private:
00247   // Should not be used 
00248   CmdLineParser(const CmdLineParser& p) { }
00249   CmdLineParser& operator=(const CmdLineParser& x) { return *this; }
00250   
00251   typedef std::map<std::string, std::string*> SwitchToArgMap;
00252   typedef std::vector<std::string> ArgVec;
00253 
00254   // Switch descriptor (Because of limited use, we allow this to be
00255   // returned as an object)
00256   class SwDesc {
00257   public:
00258     SwDesc() : isLong(false) { }
00259     SwDesc(const char* sw_, bool isLong_, const char* arg_) 
00260       : sw(sw_), isLong(isLong_), arg(arg_) { }
00261     SwDesc(const std::string& sw_, bool isLong_, const std::string& arg_) 
00262       : sw(sw_), isLong(isLong_), arg(arg_) { }
00263     ~SwDesc() { }
00264     // use default copy constructor if necessary
00265     
00266     std::string sw;  // switch text without dashes
00267     bool isLong;     // long style
00268     std::string arg; // any argument
00269   };
00270 
00271 private:
00272   void Ctor();
00273   void Reset();
00274   void CheckForErrors(const OptArgDesc* optArgDescs);
00275 
00276   const OptArgDesc* 
00277   CreateSortedCopy(const OptArgDesc* optArgDescs);
00278 
00279   // Parsing helpers
00280   SwDesc
00281   MakeSwitchDesc(const char* str);
00282   
00283   const OptArgDesc*
00284   FindOptDesc(const OptArgDesc* optArgDescs, const SwDesc& swdesc,
00285               bool errOnMultipleMatches = true);
00286   
00287   void
00288   AddOption(const OptArgDesc& odesc, const SwDesc& swdesc);
00289   
00290   void
00291   AddOption(const OptArgDesc& odesc, 
00292             const std::string& sw, const std::string& arg);
00293   
00294 private:
00295   
00296   std::string command;           // comand name
00297   SwitchToArgMap switchToArgMap; // optional arguments
00298   ArgVec arguments;              // regular arguments
00299 };
00300 
00301 
00302 #endif
00303 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines