Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Command Line Parsing with Delegates

0.00/5 (No votes)
9 Mar 2006 1  
Command line parsing with the use of delegates: the delegates support a common set of data types, with standard syntax validation.

Introduction

A quick glance at this site shows that there are several flavors of command line parsers available. Several of them are quite good. Command line parsing is the type of thing that most of us programmers do without a second thought - you've already started coding your console test application when you realize you can simplify things by passing an option or two from the command line - and the next thing you know you're typing away...

foreach (string arg in args) { if (arg.StartsWith ("-")) { blah, blah, blah...  } }

Most command line programs are put together to test a library or do a quick proof of concept. If the program is more permanent in nature, or even a GUI-based program or service that offers startup arguments, you might consider putting a framework in place that allows structured maintenance and addition of command line options. The ArgumentParser class described here fits this requirement nicely.

Background

A few weeks ago, I wrote the ArgumentParser library for use with a program I was working on. I had another program that parsed arguments manually, and while that code was clean enough, it continued to grow as I added arguments, until ultimately the largest part of the console application was the handling of the arguments! This is not necessarily unusual, since a test rig might have a pretty slim interface anyway. Still, I found myself doing some of the same things - checking for valid integers, building messages, etc. In short, I was underwhelmed.

Several things are handled by the library, rather than by the client program:

  • Catching syntax errors, such as an expected integer with an alpha character in it
  • Catching argument errors, such as a missing argument after the token
  • Tracking the occurrence of errors during argument parsing
  • Keeping the text of all errors and warnings during parsing
  • Allowing the client program to add warnings or error messages

Using the Code

The namespace AsAbove.Command.Arguments (of which ArgumentParser is a member) contains six delegate definitions. The delegates define the data types supported by the library:

public delegate string ArgumentString (string val);
public delegate string ArgumentInteger (int val);
public delegate string ArgumentDouble (double val);
public delegate string ArgumentBool (bool val);
public delegate string ArgumentDate (DateTime val);
public delegate string ArgumentSimple ();

For instance, consider a program that takes two arguments - a string and a boolean type. The purpose of the program is to indicate a change in active work status for a person, using the person's social security number. The command line for such a program might look like:

setactive -ssn 505-30-2773 -active-

The components of the command are the program name, setactive, the social security number token -ssn followed by the number, and the boolean argument -active- to indicate "inactive". Note that boolean arguments must always be terminated with a + or - sign. If the token is terminated with +, it evaluates to true, and if it is terminated with -, it evaluates to false.

The data types string, integer, double, and date must always be followed by an argument of that type; these correspond to the delegates shown above with that data type in the name. The delegate ArgumentSimple does not require a following argument - it is used in cases when the presence of the token on the command line is enough to trigger the action. For instance, this delegate would be useful for requesting help with -h or -?.

To add ArgumentParser support to a program, the first thing to do is to implement the delegate methods that are to be called for each program argument. For the setactive program, we would define two delegates:

private string SSNDelegate (string val)
{
    this.ssn_ = val;
    return "";
}
private string ActiveDelegate (bool val)
{
    this.active_ = val;
    return "";
}

Note that all delegates return a string. If the client program needs to add a message to the program output, it can return the message from the delegate.

Argument Validation by ArgumentParser

By the time the delegate is called, the argument has been validated for correct syntax. If the argument is missing or incorrect, the delegate is not called, and the error is appended to the message string contained in the ArgumentParser object. For instance, an integer argument that contains a non-digit, or a boolean argument that is not terminated with + or -.

Argument Validation by Client Delegates

The delegates have an opportunity to validate the arguments too. If a message should be given, it can be returned in a string:

private string ActiveDelegate (bool val)
{
    this.active_ = val;
    return "SSN set to " + val ? "active." : "inactive.";
}

If the delegate discovers an error condition, it can throw an exception. The ArgumentParser object catches the exception and appends the message to the message string.

private string SSNDelegate (string val)
{
    if (false == IsCorrectFormattedForSSN (val))
    {
        throw new ArgumentException ("Bad SSN format " + val);
    }
    this.ssn_ = val;
    return "";
}

After the client delegates are implemented, the next thing to do is to declare the Argument array. The Argument class stores token names for matching, the client delegate, and provides the internal methods for the syntax validation based on data type.

public Argument[] GetArgumentDefinitions
{
    get
    {
        Argument[] arguments = 
        {
            new Argument ("ssn", new ArgumentString (this.SSNDelegate)),
            new Argument ("active", new ArgumentBool (this.ActiveDelegate)) 
        };
        return arguments;
    }
}

Finally, the client program puts it all together inside of the Main method. The following code snippet is abbreviated just to show the juicy parts:

class ArgTest
{
    [STAThread]
    static void Main(string[] args)
    {        
        ArgTest a = new ArgTest ();
        ArgumentParser p = new ArgumentParser (a.GetArgumentDefinitions, args, 0);
    }
}

Points of Interest

The source code with this article contains a complete test program that demonstrates the use of each delegate type, determines if errors were found, and displays messages to the console. A help document is also included in the project.
For more information, see the readme.txt file in the Command directory.

History

  • 9th March, 2006: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here