Re: Command Parser
- From: "Peter Duniho" <NpOeStPeAdM@xxxxxxxxxxxxxxxx>
- Date: Fri, 10 Apr 2009 10:44:29 -0700
On Fri, 10 Apr 2009 08:16:02 -0700, Magius96 <Magius96@xxxxxxxxxxxxxxxxxxxxxxxxx> wrote:
Your suggestion is almost perfect, but there's one more thing that I'm
looking for that I can't seem to express properly.
Using your example function1 would be defined as:
void function1(Connection ch, UserList users, string ARGS){..}
The argument list for all the functions will always be the same, and the
return value type of all the functions will also always be the same. I
remember seeing code in C that allowed me to define the above function as:
PCMD(function1){...}
My issue is that I don't remember how PCMD would have been defined in C. I
know that in the code that I saw that 'PCMD(function1)' was somehow a
shortcut to writing the full declaration of 'void function1(Connection ch,
UserList users, string ARGS)'.
You're probably thinking of something like this:
#define PCMD(argType, argName) void function1(Connection ch, UserList users, argType argName)
IMHO, not really a great idea anyway. It obfuscates the function declaration with no real advantage, and the pre-processor macro is fragile, in that while you're allowed to provide anything for the macro's arguments, it won't compile unless they are exactly right, and the compiler error that's generated will be less-than-clear because the line generating the error doesn't quite look like the line that's actually being compiled.
If you have invariant arguments for a collection of methods, then the more OOP way to accomplish what you're doing is simply to create a new class to encapsulate the methods along with the invariant arguments. Then you can instantiate the class with the invariant arguments passed to the constructor or otherwise initialized as part of the instantiation, and then just pass the non-invariant data (the command argument) to each method when it's called.
That said, there are some confusing aspects to your original question. You state "Every command will have it's own function in another class that should be called by the command parser". But, if the method being called is in another class, you will also need a reference to the instance of that class. This means the target for the invocation itself can't be put into the Dictionary. You'll need some level of indirection, to allow you to provide the instance at the time of the call. For example (I'm using the generic Action delegate type here, because it seems more appropriate to me than declaring your own, but otherwise trying to stick to Alberto's conventions):
Dictionary<string, Action<object, string>> commandTable;
void BuildCommandTable()
{
commandTable = new Dictionary<string, Action<object, string>>();
commandTable.Add("FirstCommand", (exec, args) => ((ExecutorA)exec).function1(args));
commandTable.Add("SecondCommand", (exec, args) => ((ExecutorB)exec).function2(args));
// ...etc...
}
void InterpretCommand(string command, string args, object exec)
{
Action<Executor, string> action;
// Using TryGetValue avoids an exception in the event of an
// invalid command. You may or may not want this.
if (commandTable.TryGetValue(command, out action))
{
action(exec, args);
}
}
Of course, if the type of the "executor" is always the same, then you can avoid the casting:
Dictionary<string, Action<Executor, string>> commandTable;
void BuildCommandTable()
{
commandTable = new Dictionary<string, Action<object, string>>();
commandTable.Add("FirstCommand", (exec, args) => exec.function1(args));
commandTable.Add("SecondCommand", (exec, args) => exec.function2(args));
// ...etc...
}
void InterpretCommand(string command, string args, Executor exec)
{
Action<Executor, string> action;
// Using TryGetValue avoids an exception in the event of an
// invalid command. You may or may not want this.
if (commandTable.TryGetValue(command, out action))
{
action(exec, args);
}
}
Either of those approaches allows a target instance to be passed along with the command and arguments, so that the method call can be directed to the appropriate instance.
The above technique can be combined with the other suggestion of wrapping invariants in a new class, as appropriate to your actual design.
I'm not familiar with the Dictionary type, so I'll have to ask some
questions on it. Eventually I'll have to implment a near match algorythm.
Meaning that if a user types 'sta' and the only command that starts with
those three letters is 'status' then I want the parser to call the
cooresponding function. I can implement the algorythm myself, but can the
Dictionary type handle that, or what would be a better array type to use for
that functionality?
As Alberto says, this isn't built into the Dictionary. But, it's simple enough to use the Dictionary in a way that supports it. Just use the first three letters of the command as the key, and maintain a Dictionary of values for that key that you can search through. Using the Dictionary for the second level of indirection is a little "heavy", but it provides a convenient way to pair up the actual command name and the delegate that will execute the command.
For example (again, sticking as closely as possible to previous code examples):
Dictionary<string, Dictionary<string, Action<string>>> commandTable;
void BuildCommandTable()
{
commandTable = new Dictionary<string, Dictionary<string, Action<string>>>();
}
void AddCommand(string name, Action<string> action)
{
string strPrefix = name.Substring(0, 3);
Dictionary<string, Action<string>> dictPrefix;
if (!commandTable.TryGetValue(strPrefix, out dictPrefix))
{
dictPrefix = new Dictionary<string, Action<string>>();
commandTable.Add(strPrefix, dictPrefix);
}
dictPrefix.Add(name, action);
}
void InterpretCommand(string name, string args)
{
Dictionary<string, Action<string>> dictPrefix;
Action<string> action;
if (commandTable.TryGetValue(name.Substring(0, 3)), out dictPrefix) &&
dictPrefix.TryGetValue(name, out action))
{
action(args);
}
}
Of course, now that I wrote all that I realize that you might not have meant that you literally are always checking the first three letters. I've never heard of a "dicothomic search", but assuming Alberto means something like a binary search, then his suggestion makes sense for a more general-purpose solution (including the advice to not bother with the sort optimization unless you have a large number of commands to worry about...otherwise a linear search is simpler to implement and plenty fast).
Also, there may at some point be more fields in the
command list than just the command and the function. Can the Dictionary type
handle that as well?
As Alberto says, the Dictionary is strictly a key/value pairing. But, you can put whatever you like as the value. As long as you can encapsulate your mapped data as some single value, it can be put into the Dictionary.
Pete
.
- Follow-Ups:
- Re: Command Parser
- From: Alberto Poblacion
- Re: Command Parser
- References:
- Command Parser
- From: Magius96
- Re: Command Parser
- From: Alberto Poblacion
- Re: Command Parser
- From: Magius96
- Command Parser
- Prev by Date: Re: .NET/C# versus PHP
- Next by Date: Re: Visual Studio Professional Edition purchase
- Previous by thread: Re: Command Parser
- Next by thread: Re: Command Parser
- Index(es):
Relevant Pages
|
Loading