Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members

argv_parser.h

00001 #ifndef s11n_ARGV_PARSER_H
00002 #define s11n_ARGV_PARSER_H 1
00003 
00004 // Author: stephan@s11n.net
00005 // License: Public Domain
00006 
00007 #include "property_store.h"
00008 namespace s11n {
00009         /**
00010            argv_parser is an object for parsing command line options. It exists
00011            in the s11n source tree only to ease development of test apps,
00012            and should not be considered to be part of the core library.
00013 
00014 
00015 
00016            Use it like this:
00017 
00018            <pre>
00019 #include &lt;iostream&gt;
00020 #include "argv_parser.h"
00021 
00022 #define VERBOSE if( opts.get( "verbose", false ) ) cerr
00023 
00024 int main( int argc, char **argv )
00025 {
00026     argv_parser & opts = argv_parser::args( argc, argv );
00027         opts.set( "dosomething", ! opts.get_bool( "donothing", false ) );
00028         if( ! opts.get( "dosomething", true ) ) { exit( 0 )); }
00029         int myint = opts.get( "width", 20 );
00030         double myangle = opts.get( "angle", 0.0 );
00031 
00032         VERBOSE << "This is a verbose message." << endl;
00033         return opts.get( "errorcode", 0 );
00034 }
00035 </pre>
00036            (that code's just off the top of my head - it may not compile as-is.)
00037 
00038            Note that client code outside of main can then get access
00039            to the args via the static function args():
00040 
00041            <pre>
00042 argv_parser & opts = argv_parser::args();
00043            </pre>
00044 
00045            See the property_store object's API for a full list of accessor functions.
00046 
00047            Supported command-line formats:
00048            <pre>
00049 -foo bar [that is, -foo == "bar"]
00050 is the same as:
00051 [--foo bar] [-foo=bar] [--foo=bar]
00052 
00053 -foo -bar false [-foo == true, -bar == false]
00054 -foo -bar=false [same]
00055 --double-dashed "some string value"
00056 --double-dashed="some string value" [same as previous line]
00057            </pre>
00058 
00059            Whether you use single or double dashes is irrelevant, but you must call
00060            get() with the same key as was actually passed on the command-line,
00061            like so:
00062 
00063            int width = opts.getInt( "-width", opts.getInt( "--width", 42 ) );
00064            will check for -width first, then --width, defaulting to 42.
00065 
00066            Alternately, if you use this form:
00067            opts.get_string( "foo" ); // WITHOUT a dash
00068            the following options are searched:
00069 <ol>
00070 <li>foo
00071 <li>-foo
00072 <li>--foo
00073 </ol>
00074 
00075            so the above call may actually result in getting the value from -foo or
00076            --foo. This is a potential logic problem if your application uses two
00077            semantically-different, like-named arguments like -force and
00078            --force. In this case a call to get( "force" ) would find -force
00079            and not --force. Use get( "-force" ) or get("--force") to
00080            avoid this ambiguity. The dashed versions of an argument are only
00081            sought after if get() is called without a dash before the key.
00082 
00083            These sample dash-searching rules apply to is_set().
00084 
00085 
00086            A note to Qt users: call args() on this object before calling
00087            QApplication a(argc, argv ), or QApplication will steal any argument
00088            called -name (and possibly others), removing it from argv. i.e.,
00089            if you do not call args() on this object first, QApplication may
00090            steal arguments so you'll never see them. Been there, done that.
00091 
00092            Known Bugs and/or gotchyas:
00093 
00094            Negative numbers:
00095 
00096            <pre>
00097 --boolflag -123=something
00098            </pre>
00099 
00100            will be parsed as:
00101 
00102            <pre>
00103 [--boolflag == true] [-123=something]
00104            </pre>
00105 
00106            Search the .cpp file for 'negative' and you'll find where this bug lives.
00107            Since numeric arguments are so rare this is not such a big deal, i think.
00108            i can think of only a few real-world examples which use args like -1:
00109            ssh, [GNU] xargs, head, tail, lpr, ... okay, maybe they aren't so uncommon :/
00110 
00111            Along those same lines:
00112 
00113            <pre>
00114 --bool -234 --foobar
00115            </pre>
00116            will be parsed as:
00117            <pre>
00118 [--bool == -234] [--foobar == true]
00119            </pre>
00120 
00121            Which i consider to be correct for most cases. If you want to set
00122            --bool to a negative number use: --bool=-123
00123            If you want to force a boolean value in this case:
00124 
00125            <pre>
00126 --bool=true -234 546
00127            </pre>
00128 
00129            parses as:
00130 
00131            <pre>
00132 --bool=true
00133 -234=456
00134            </pre>
00135 
00136            i hate the inconsistency this adds, though. :/
00137         */
00138 
00139         class argv_parser : public property_store
00140         {
00141         public:
00142                 /**
00143                    Creates a new parser using the given
00144                    arguments array and arg count.
00145                 */
00146                 argv_parser( int argc, char *argv[], int startAt=0 );
00147                 argv_parser();
00148                 virtual ~argv_parser();
00149 
00150                 /**
00151                    args( int, char * ) should be called once from your
00152                    main() if you want clients to be able to use args()
00153                    to get at them.
00154                 */
00155                 static argv_parser & args( int argc, char *argv[] );
00156 
00157                 /**
00158                    Returns the object containing the arguments
00159                    supplied to args(int,char**).
00160                 */
00161                 static argv_parser & args();
00162 
00163                 /**
00164                    get/set_help() text for a given key.
00165                 */
00166                 virtual void set_help( const std::string &key, const std::string &text );
00167 
00168                 /**
00169                    Returns the help text associated with key.
00170                  */
00171                 virtual const std::string getHelp( const std::string &key ) const;
00172 
00173                 /**
00174                    Re-implemented to check keys -FOO and --FOO if key FOO is
00175                    not found.
00176                 */
00177                 virtual bool is_set( const std::string &key ) const;
00178 
00179                 /**
00180                    get_string() is overridden to add a special case to
00181                    all get() calls made via the property_store API: if
00182                    a get() function is called with a key which does
00183                    not start with a dash (-) character and they key
00184                    cannot be found in our list then -key and --key
00185                    will be tried.
00186 
00187                    This means that, assuming the above sample code is
00188                    in place, the following would work:
00189 
00190 <pre>
00191 ~/ > myapp --foo=17.34
00192 ...
00193 double d = opts.getDouble( "foo", 0.0 ); // d == 17.34
00194 </pre>
00195 
00196                    As will this:
00197 <pre>
00198 opts.set( "--mykey", "myvalue" );
00199 ...
00200 cerr << "mykey="<< opts.get_string( "mykey" ) << endl;
00201 </pre>
00202 
00203                    Note, however, that command-line arguments passed
00204                    without a leading dash are not treated as
00205                    arguments, and will not be inside this object if
00206                    the command-line arguments are passed in via
00207                    args().  Additionally, it is important to note that
00208                    if key is passed in with a leading "-" then the
00209                    additional "dash checking" is NOT done. That is, if
00210                    you call:
00211 
00212 <pre>
00213 opts.get_string( "-d", 0.0 );
00214 </pre>
00215 
00216                    then ONLY the entry -d will match, and not --d.
00217                 */
00218                 virtual std::string get_string( const std::string &key, const std::string & defaultVal = std::string() ) const;
00219 
00220                 /**
00221                  * Makes a half-hearted attempt to parse out any args (begining with "-").
00222                  * Any args without values passed after them are assigned the value true.
00223                  * Sample valid command lines:
00224                  *
00225                    <pre>
00226                  * foo --a --b foo --c bar --f
00227                  *   (--a and --f == true, --b == "foo" and --c == "bar")
00228                  * foo --a eat --b this --c "you lazy bum"
00229                  *   (--a==eat, --b==this, --c=="you lazy bum")
00230                  * foo --a=b --c d
00231                  *   (--a == b, --c == d)
00232                    </pre>
00233                  *
00234                  * These are identical for purposes of get( "c" ):
00235                    <pre>
00236                  * [... -c=-1.0 ...] [... -c -1.0 ...] [... --c 1.0 ...] [... --c=1.0 ...]
00237                    </pre>
00238                  *
00239                  *
00240                  * To get the values, call the property_store API functions like:
00241                    <pre>
00242                  *    int foo = parser.getInt( "--i" ); // getInt("i") now works for -i or --i :)
00243                  *    bool bar = parser.get_bool( "--b" ); // or get_bool( "b") for -b or --b
00244                    </pre>
00245                  * 'startat' says which element in argv[] to start with.
00246                  *
00247                  * If argpre = 0 then it uses the default argument prefix (defaults to "-"). 
00248                  * If it is >0 then that is used as a char * prefix
00249                  * for all arguments.
00250                  *
00251                  * This function also stores all processed values in a way familiar to bash and perl users:
00252                  * $0 = the first argument,
00253                  * $1 = the second arg
00254                  * etc.
00255                  * Thus given:
00256                    <pre>
00257                  *     ./foo --arg1 val1 --arg2=foo
00258                    </pre>
00259                  * We have:
00260                    <pre>
00261                  *  myargs["$1"] == "--arg1"
00262                  *  myargs["$3"] == "--arg2=foo" (it is arguably useful to split these, but then usage would be
00263                  *                                inconsistent with bash/perl. However, as it is now it is inconsistent
00264                  *                                with the results of "--arg2 foo" :/)
00265                    </pre>
00266                  * Note that the values are added to this object (or overwrite existing entries), and the list
00267                  * is not cleared by this function.
00268                  */
00269                 virtual int args( int argc, char *argv[], int startAt, const char *argpre = "-" );
00270 
00271                 /**
00272                  * Similar to parse( int... ) except that this one reads a whole line of options, parses that into
00273                  * an array, then passes it to parse(...). Note that this _may_ (but should not, ideally) behave slightly
00274                  * differently from arguments passed to the other form, which typically come in as command-line args
00275                  * (parsed by your shell). This functions uses a stdstring_tokenizer to do it's parsing, so
00276                  * any differences in behaviour should be resolved there. i am not aware of any differences.
00277                  */
00278                 virtual int args( std::string args, std::string separators = " " );
00279 
00280                 /**
00281                    Creates a "usage"-like string for this object containing all keys for which
00282                    set_help() has been called. If showcurrentvals is true then the current values
00283                    are also added to th string, otherwise they are left out.
00284 
00285                    Note that the order of the dumped help text/keys is
00286                    alphabetic (because that's how the container object
00287                    stores them).
00288        
00289                    TODO: maintain the order of args, using the order from set_help().
00290 
00291                    Sample:
00292 
00293                    <pre>
00294 in main() do:
00295 argv_parser & args = argv_parser::args( argc, argv );
00296 if( args.is_hel_set() )
00297 { // triggers on --help, -help, -? or --?
00298     cerr << args.dump_help();
00299         exit( 0 ); // some apps exit with a non-zero for help, some don't.
00300 }
00301                    </pre>
00302                 */
00303                 virtual const std::string dump_help( bool showcurrentvals = true ) const;
00304 
00305                 /**
00306                    Returns true if -help --help, -? or --? is set.
00307                 */
00308                 virtual bool is_help_set();
00309         
00310         private:
00311                 property_store helpmap;
00312         };
00313 }; // namespace s11n
00314 #endif // s11n_ARGV_PARSER_H

Generated on Tue Oct 26 18:25:59 2004 for s11n by  doxygen 1.3.9.1