From c1ae40c18011965a69342b8fb5e9632a2cfacc43 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 7 Aug 2002 18:27:04 +0000 Subject: [PATCH] * Write the "Custom parser" section * Boldify stuff that changes in the help output. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@3254 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/CommandLine.html | 309 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 253 insertions(+), 56 deletions(-) diff --git a/docs/CommandLine.html b/docs/CommandLine.html index b3e9840b1fc..e9546cb2f56 100644 --- a/docs/CommandLine.html +++ b/docs/CommandLine.html @@ -16,6 +16,7 @@ set of possibilities
  • Named alternatives
  • Parsing a list of options +
  • Adding freeform text to help output
  • Reference Guide
      @@ -36,8 +37,10 @@ specified
    1. Controlling other formatting options -
    2. Option Classes +
    3. Top-Level Classes and Functions +
      Introduction
        @@ -108,7 +111,7 @@ because the application doesn't have to keep a "list" of arguments to pass to the parser. This also makes supporting dynamically loaded options trivial.

        -

      • More Clean: CommandLine supports enum and other types directly, meaning that +
      • Cleaner: CommandLine supports enum and other types directly, meaning that there is less error and more security built into the library. You don't have to worry about whether your integral command line argument accidentally got assigned a value that is not valid for your enum type.

        @@ -169,7 +172,7 @@ Additionally, you need to add this as the first line of your main program:

         int main(int argc, char **argv) {
        -  cl::ParseCommandLineOptions(argc, argv);
        +  cl::ParseCommandLineOptions(argc, argv);
           ...
         }
         

        @@ -207,7 +210,7 @@ USAGE: compiler [options] OPTIONS: -help - display available options (--help-hidden for more) - -o <filename> - Specify output filename + -o <filename> - Specify output filename Because we specified that the command line option should parse using the @@ -226,8 +229,8 @@ There are many different options that you can use to customize the command line option handling library, but the above example shows the general interface to these options. The options can be specified in any order, and are specified with helper functions like cl::desc(...), so -there are no positional dependencies to have to remember. The available options -are discussed in detail in the Reference Guide.

        +there are no positional dependencies to remember. The available options are +discussed in detail in the Reference Guide.

        Continuing the example, we would like to have our compiler take an input @@ -273,7 +276,7 @@ adding one of the declarations above, the --help option synopsis is now extended to:

        -USAGE: compiler [options] <input file>
        +USAGE: compiler [options] <input file>
         
         OPTIONS:
           -help             - display available options (--help-hidden for more)
        @@ -283,6 +286,7 @@ OPTIONS:
         ... indicating that an input filename is expected.

        +

         Boolean Arguments @@ -307,13 +311,14 @@ href="#cl::Hidden">cl::Hidden" flag. This modifier prevents it from being shown by the standard "--help" output (note that it is still shown in the "--help-hidden" output).

      -The CommandLine library uses a different parser for different data types. For -example, in the string case, the argument passed to the option is copied -literally into the content of the string variable... we obviously cannot do that -in the boolean case, however, so we must use a smarter parser. In the case of -the boolean parser, it allows no options (in which case it assigns the value of -true to the variable), or it allows the values "true" or -"false" to be specified, allowing any of the following inputs:

      +The CommandLine library uses a different parser +for different data types. For example, in the string case, the argument passed +to the option is copied literally into the content of the string variable... we +obviously cannot do that in the boolean case, however, so we must use a smarter +parser. In the case of the boolean parser, it allows no options (in which case +it assigns the value of true to the variable), or it allows the values +"true" or "false" to be specified, allowing any of the +following inputs:

        compiler -f          # No value, 'Force' == true
      @@ -322,11 +327,12 @@ true to the variable), or it allows the values "true" or
        compiler -f=FALSE    # Value specified, 'Force' == false
       
      -... you get the idea. The bool parser just turns the string values into boolean -values, and rejects things like 'compiler -f=foo'. Similarly, the -float, double, and int parsers work like you would expect, using the -'strtol' and 'strtod' C library calls to parse the string -value into the specified data type.

      +... you get the idea. The bool parser just turns the +string values into boolean values, and rejects things like 'compiler +-f=foo'. Similarly, the float, double, and int parsers work +like you would expect, using the 'strtol' and 'strtod' C +library calls to parse the string value into the specified data type.

      With the declarations above, "compiler --help" emits this:

      @@ -334,21 +340,21 @@ With the declarations above, "compiler --help" emits this:

      USAGE: compiler [options] <input file> OPTIONS: - -f - Overwrite output files + -f - Overwrite output files -o - Override output filename - -quiet - Don't print informational messages + -quiet - Don't print informational messages -help - display available options (--help-hidden for more)

      and "opt --help-hidden" prints this:

      -USAGE: opt [options] <input file>
      +USAGE: compiler [options] <input file>
       
       OPTIONS:
         -f     - Overwrite output files
         -o     - Override output filename
      -  -q     - Don't print informational messages
      +  -q     - Don't print informational messages
         -quiet - Don't print informational messages
         -help  - display available options (--help-hidden for more)
       

      @@ -473,11 +479,11 @@ help output now is:

      USAGE: compiler [options] <input file> OPTIONS: - Choose optimization level: + Choose optimization level: -g - No optimizations, enable debugging -O1 - Enable trivial optimizations -O2 - Enable default optimizations - -O3 - Enable expensive optimizations + -O3 - Enable expensive optimizations -f - Overwrite output files -help - display available options (--help-hidden for more) -o <filename> - Specify output filename @@ -556,10 +562,10 @@ OPTIONS: -O1 - Enable trivial optimizations -O2 - Enable default optimizations -O3 - Enable expensive optimizations - -debug_level - Set the debugging level: + -debug_level - Set the debugging level: =none - disable debug information =quick - enable quick debug information - =detailed - enable detailed debug information + =detailed - enable detailed debug information -f - Overwrite output files -help - display available options (--help-hidden for more) -o <filename> - Specify output filename @@ -642,6 +648,48 @@ checking we have to do.

      + +
         +Adding freeform text to help output +

        + +As our program grows and becomes more mature, we may decide to put summary +information about what it does into the help output. The help output is styled +to look similar to a Unix man page, providing concise information about +a program. Unix man pages, however often have a description about what +the program does. To add this to your CommandLine program, simply pass a third +argument to the cl::ParseCommandLineOptions +call in main. This additional argument is then printed as the overview +information for your program, allowing you to include any additional information +that you want. For example:

        + +

        +int main(int argc, char **argv) {
        +  cl::ParseCommandLineOptions(argc, argv, " CommandLine compiler example\n\n"
        +                              "  This program blah blah blah...\n");
        +  ...
        +}
        +

        + +Would yield the help output: + +

        +OVERVIEW: CommandLine compiler example
        +
        +  This program blah blah blah...
        +
        +USAGE: compiler [options] <input file>
        +
        +OPTIONS:
        +  ...
        +  -help             - display available options (--help-hidden for more)
        +  -o <filename>     - Specify output filename
        +

        + + + +

      Reference Guide @@ -675,7 +723,7 @@ Given these two option declarations, the --help output for our grep replacement would look like this:

      -USAGE: spiffygrep [options] <regular expression> <input file>
      +USAGE: spiffygrep [options] <regular expression> <input file>
       
       OPTIONS:
         -help - display available options (--help-hidden for more)
      @@ -751,11 +799,11 @@ shell itself.  Using the CommandLine library, we would specify this as:

      which automatically provides the help output:

      -USAGE: spiffysh [options] <input script> <program arguments>...
      +USAGE: spiffysh [options] <input script> <program arguments>...
       
       OPTIONS:
         -help - display available options (--help-hidden for more)
      -  -x    - Enable trace output
      +  -x    - Enable trace output
       

      At runtime, if we run our new shell replacement as 'spiffysh -x test.sh -a @@ -853,7 +901,9 @@ This section describes the basic attributes that you can specify on options.

      href="#positional">positional options) specifies what the option name is. This option is specified in simple double quotes:

      -cl::opt<bool> Quiet("quiet");

      +

      +cl::opt<bool> Quiet("quiet");
      +

    4. The cl::desc attribute specifies a description for the option to be shown in the --help output for the @@ -1105,13 +1155,31 @@ basically looks like this:

         -Option Classes +Top-Level Classes and Functions


      The +cl::ParseCommandLineOptions function


      The cl::opt class

        @@ -1122,10 +1190,10 @@ can take up to three arguments (all except for the first have default values though):

        -namespace cl {
        -  template <class DataType, bool ExternalStorage = false,
        -            class ParserClass = parser<DataType> >
        -  class opt;
        +namespace cl {
        +  template <class DataType, bool ExternalStorage = false,
        +            class ParserClass = parser<DataType> >
        +  class opt;
         }
         

        @@ -1151,10 +1219,10 @@ line options. It too is a templated class which can take up to three arguments:

        -namespace cl {
        -  template <class DataType, class Storage = bool,
        -            class ParserClass = parser<DataType> >
        -  class list;
        +namespace cl {
        +  template <class DataType, class Storage = bool,
        +            class ParserClass = parser<DataType> >
        +  class list;
         }
         

        @@ -1171,8 +1239,8 @@ The cl::alias class is a nontemplated class that is used to form aliases for other arguments.

        -namespace cl {
        -  class alias;
        +namespace cl {
        +  class alias;
         }
         

        @@ -1238,26 +1306,155 @@ exponential notation (ex: 1.7e15) and properly supports locales.
      • -TODO +Although the CommandLine library has a lot of functionality built into it +already (as discussed previously), one of its true strengths lie in its +extensibility. This section discusses how the CommandLine library works under +the covers and illustrates how to do some simple, common, extensions.

        + -

         -Writing a custom parser + +
         Writing a custom parser
        +One of the simplest and most common extensions is the use of a custom parser. +As discussed previously, parsers are the portion +of the CommandLine library that turns string input from the user into a +particular parsed data type, validating the input in the process.

        + +There are two ways to use a new parser:

        + +

          +
        1. Specialize the cl::parser template for + your custom data type.

          + + This approach has the advantage that users of your custom data type will + automatically use your custom parser whenever they define an option with a + value type of your data type. The disadvantage of this approach is that it + doesn't work if your fundemental data type is something that is already + supported.

          + +

        2. Write an independant class, using it explicitly from options that need + it.

          + + This approach works well in situations where you would line to parse an + option using special syntax for a not-very-special data-type. The drawback + of this approach is that users of your parser have to be aware that they are + using your parser, instead of the builtin ones.

          + +

        + +To guide the discussion, we will discuss a custom parser that accepts file +sizes, specified with an optional unit after the numeric size. For example, we +would like to parse "102kb", "41M", "1G" into the appropriate integer value. In +this case, the underlying data type we want to parse into is +'unsigned'. We choose approach #2 above because we don't want to make +this the default for all unsigned options.

        + +To start out, we declare our new FileSizeParser class:

        + +

        +struct FileSizeParser : public cl::basic_parser<unsigned> {
        +  // parse - Return true on error.
        +  bool parse(cl::Option &O, const char *ArgName, const std::string &ArgValue,
        +             unsigned &Val);
        +};
        +

        + +Our new class inherits from the cl::basic_parser template class to fill +in the default, boiler plate, code for us. We give it the data type that we +parse into (the last argument to the parse method so that clients of +our custom parser know what object type to pass in to the parse method (here we +declare that we parse into 'unsigned' variables.

        + +For most purposes, the only method that must be implemented in a custom parser +is the parse method. The parse method is called whenever the +option is invoked, passing in the option itself, the option name, the string to +parse, and a reference to a return value. If the string to parse is not well formed, the parser should output an error message and return true. Otherwise it should return false and set 'Val' to the parsed value. In our example, we implement parse as:

        + +

        +bool FileSizeParser::parse(cl::Option &O, const char *ArgName,
        +                           const std::string &Arg, unsigned &Val) {
        +  const char *ArgStart = Arg.c_str();
        +  char *End;
        + 
        +  // Parse integer part, leaving 'End' pointing to the first non-integer char
        +  Val = (unsigned)strtol(ArgStart, &End, 0);
        +
        +  while (1) {
        +    switch (*End++) {
        +    case 0: return false;   // No error
        +    case 'i':               // Ignore the 'i' in KiB if people use that
        +    case 'b': case 'B':     // Ignore B suffix
        +      break;
        +
        +    case 'g': case 'G': Val *= 1024*1024*1024; break;
        +    case 'm': case 'M': Val *= 1024*1024;      break;
        +    case 'k': case 'K': Val *= 1024;           break;
        +
        +    default:
        +      // Print an error message if unrecognized character!
        +      return O.error(": '" + Arg + "' value invalid for file size argument!");
        +    }
        +  }
        +}
        +

        + +This function implements a very simple parser for the kinds of strings we are +interested in. Although it has some holes (it allows "123KKK" for +example), it is good enough for this example. Note that we use the option +itself to print out the error message (the error method always returns +true) in order to get a nice error message (shown below). Now that we have our +parser class, we can use it like this:

        + +

        +static cl::opt<unsigned, false, FileSizeParser>
        +MFS("max-file-size", cl::desc("Maximum file size to accept"),
        +    cl::value_desc("size"));
        +

        + +Which adds this to the output of our program:

        + +

        +OPTIONS:
        +  -help                 - display available options (--help-hidden for more)
        +  ...
        +  -max-file-size=<size> - Maximum file size to accept
        +

        + +And we can test that our parse works correctly now (the test program just prints +out the max-file-size argument value):

        + +

        +$ ./test
        +MFS: 0
        +$ ./test -max-file-size=123MB
        +MFS: 128974848
        +$ ./test -max-file-size=3G
        +MFS: 3221225472
        +$ ./test -max-file-size=dog
        +-max-file-size option: 'dog' value invalid for file size argument!
        +

        + +It looks like it works. The error message that we get is nice and helpful, and +we seem to accept reasonable file sizes. This wraps up the "custom parser" +tutorial.

        -

         -Exploiting external storage -
        +
      +
         Exploiting external +storage
        -
         -Dynamically adding command line options -
        +
      +
         Dynamically adding command +line options
        @@ -1272,7 +1469,7 @@ TODO
        Chris Lattner
        -Last modified: Tue Aug 6 14:34:47 CDT 2002 +Last modified: Wed Aug 7 13:22:40 CDT 2002
        -- 2.34.1