tags:

views:

609

answers:

9

I have a program that can have a lot of parameters (we have over +30 differents options).

Example: myProgram.exe -t alpha 1 -prod 1 2 -sleep 200

This is 3 Commands (from command pattern object at the end) that each contain some parameters. Inside the code we parse all command (start with -) and get a list of string (split all space) for the parameters. So in fact, we have : string-->Collection of String parameters for each command.

For the moment, we use string comparison and we can get the whole thing works (instance the concrete command and return the ICommand interface). The problem is we require to do a lot of IF everytime to get the good command.

Do you have some pattern that can be used to extract all parameters from an EXE without using a lot of IF?

The code is in C# but I think the logic can be any other language too...

+6  A: 

(Well, since this is tagged with Python):

We use Python's optparse module for this purpose. It has a much friendlier API than lots of ifs.

Ali A
+1  A: 

I'd be a little uncomfortable using a command line like that. First thing I'd say is "what does the first '1' mean, and why is it different to the second '1'?"

Any time I write a command line utility that accepts an argument, I consider how easy it would be for a user to learn all the options. In this case, it looks like a bit of a hard task, IMHO.

Maybe refactoring how the user passes the arguments would be a good idea. There's a reason why a lot of software takes key/value type parameters (e.g. myclient.exe -server=myServerName -config=debug) It takes a lot of load off the user, and also simplifies the argument parsing once it hits your code.

ZombieSheep
+1 thx for the ideas this is something that can be implemented.
Daok
+5  A: 

Create a hash table which stores function pointers (in C# that'd be delegates) for handling each of the parameters, keyed using the parameter text. Then you just go through the command line in a loop and make calls to delegates based on what comes out of hash table lookups.

SoapBox
+1 nice idea too. I'll think about it.
Daok
What's the use of a modern high level language, if you then go and do all the work yourself?
hop
+4  A: 

The getopt function is very common for C programming. It can parse parameters for you. Here is a question (and answer) where to get it for C#: http://stackoverflow.com/questions/172443/getopt-library-for-c .

Especially lappies implementation looks like rather modern C# with attributes and such.

Johannes Schaub - litb
+4  A: 

A useful options library for C#: NDesk.Options

dpp
+1 great but it's written in the website that it's unstable so I might consider something else.
Daok
I've used it without trouble. Also see this blog entry from Miguel de Icaza: http://tirania.org/blog/archive/2008/Oct-14.html. It's going to be distributed with the next release of Mono.
dpp
A: 

I dont think this is too cludging..

private void Main() 
{ 
string c = "-t alpha 1 -prod 1 2 -sleep 200"; 

foreach (string incommand in Strings.Split(c, "-")) { 
    HandleCommand(Strings.Split(incommand.Trim, " ")); 
} 
} 


public void HandleCommand(string[] c) 
{ 
switch (c(0).ToLower) { 
    case "t": 
        Interaction.MsgBox("Command:" + c(0) + " params: " + c.Length - 1); 
        break; 
    case "prod": 
        Interaction.MsgBox("Command:" + c(0) + " params: " + c.Length - 1); 
        break; 
    case "sleep": 
        Interaction.MsgBox("Command:" + c(0) + " params: " + c.Length - 1); 
        break; 
} 
}

Of course, instead of doing exactly same thing in those switch-statements, call appropriate functions or code.

Stefan
This look similar to what we are doing (except that we do not use CASE but IF/ELSEIF). I am trying to find a way without using a lot of comparison.
Daok
Somewhere you have to do the dirty work.. you can hide it in objects that make it nicer to work with and map it to strong names in objects but I did'nt think that was your question.You could make a ParserClass that you feed the string and then have strong name like .prod.arguments on and so on.
Stefan
+1  A: 

Just for fun, you can create a wrapper around the whole thing and work with strong names in your code. More work? Yes. But more fun and once you add a new command to the wrapper you can forget about it ;)

public class Form1 
{ 

private void main() 
{ 
    MyCommandHandler CommandLineHandler = new MyCommandHandler(); 
    CommandLineHandler.SetInput = "-t alpha 1 -prod 1 2 -sleep 200"; 

    //now we can use strong name to work with the variables: 
    //CommandLineHandler.prod.ProdID 
    //CommandLineHandler.prod.ProdInstanceID 
    //CommandLineHandler.Alpha.AlhaValue() 
    //CommandLineHandler.Sleep.Miliseconds() 
    if (CommandLineHandler.Alpha.AlhaValue > 255) { 
        throw new Exception("Apha value out of bounds!"); 
    } 

} 
} 

public class MyCommandHandler 
{ 
private string[] values; 
public string SetInput { 
    set { values = Strings.Split(value, "-"); } 
} 

//Handle Prod command 
public struct prodstructure 
{ 
    public string ProdID; 
    public string ProdInstanceID; 
} 
public prodstructure prod { 
    get { 
        prodstructure ret = new prodstructure(); 
        ret.ProdID = GetArgsForCommand("prod", 0); 
        ret.ProdInstanceID = GetArgsForCommand("prod", 1); 
        return ret; 
    } 
} 

//Handle Apha command 
public struct Aphastructure 
{ 
    public int AlhaValue; 
} 
public Aphastructure Alpha { 
    get { 
        Aphastructure ret = new Aphastructure(); 
        ret.AlhaValue = Convert.ToInt32(GetArgsForCommand("alpha", 0)); 
        return ret; 
    } 
} 


//Handle Sleep command 
public struct SleepStructure 
{ 
    public int Miliseconds; 
} 
public SleepStructure Sleep { 
    get { 
        SleepStructure ret = new SleepStructure(); 
        ret.Miliseconds = Convert.ToInt32(GetArgsForCommand("sleep", 0)); 
        return ret; 
    } 
} 


private string GetArgsForCommand(string key, int item) 
{ 
    foreach (string c in values) { 
        foreach (string cc in Strings.Split(c.Trim, " ")) { 
            if (cc.ToLower == key.ToLower) { 
                try { 
                    return Strings.Split(c.Trim, " ")(item + 1); 
                } 
                catch (Exception ex) { 
                    return ""; 
                } 
            } 
        } 
    } 
    return ""; 
} 
}
Stefan
+1  A: 

The answer in Java, as is so often the case, is that someone has beaten you to it and released an excellent open source library to do this. Have a look at Apache CLI.

serg10
A: 

Commonly, you can replace large if/else or switch/case constructs with a Dictionary. Where the if-criteria is the key and the code to execute is the value.

For example, you could nave a Dictionary<string, ICommand> (or Dictionary<string, Type>), which you fill before you parse your command line.

When you iterate over the passed in command line options, you simply look them up in the dictionary and "invoke" on the value which is the matching command (object) to execute (or alternatively use Activate.CreateInstance(/*dictionary-value*/) if you stored the type instead of a specific object instance).

In C# 3.0 you could also something like Dictionary<string, System.Linq.Expressions.Expression<T>>, although this gets you pretty close to the actual if-statement - which is something you might want to have or not. YMMV.

Some libraries provide you with the mere parsing of the command line arguments (like traditionally getopt() et al did) or can provide the whole package, including the invokation of actions upon the presence of specific command line arguments.

Christian.K