need a solution to decides logic at runtime

Started by
23 comments, last by _moagstar_ 14 years, 9 months ago
What I meant by saying I would not call C# a scripting language is that I would put it at the same level of C++ and not something like PHP which I consider to be more of a scripting language than a full programming language (but lets not get into that here).

Funny someone mentioned Unity as that is the engine I am designing this game console for as they have no in game console solution available.

The if/else if (or switch) solution is not something I would consider using because it is quite limiting. I plan this game console to be able to run any type of command simple or complex and want it to support unlimited number of commands. Also with that solution, adding custom commands (and since the main reason for starting this project is to allow easier testing so I would except people to want to added custom commands) is next to impossible to maintain if I want to be able to continually release updates as they would need to modify the core code.
Advertisement
Mostly related, but if you have a lot of if / else string comparisons it can get pretty slow if you have a lot of strings (as probably has been pointed out).

A quick and easy solution to speed things back up is to do a switch on the first letter first ie:

//assuming Command has been converted to lowercaseswitch(Command[0]){  case 'a':  {    if(!strcmp(Command,"authenticate"))      Authenticate(Params);    else if(!strcmp(Command,"authorize"))      Authorize(Params);    break;  }  .....  case 'q':  {    if(!strcmp(Command,"query"))      Query(Params);    else if(!strcmp(Command,"quit"))      Quit(Params);    break;  }}
Ugh - this is C#. Don't reinvent the wheel or use hacks. Dictionary is probably just as fast as any if/else or switch statement, and scales without effort to tens of thousands of entries with no maintainability penalty.

Delegates solve the problem of binding, and it's trivial to add multiple handlers should it prove convenient.

And the string routines handle the parsing of input. And if anyone is concerned about speed of parsing or lookup: How many commands can a human enter per second?

Command parsing needs to be trivially maintainable - it will never become a performance bottleneck, even if a person types hundreds of commands per second, or they happen to be loaded from a file or something - the overhead of processing will never be an issue.

Even if used for run-time dispatch, doing tens of thousands of lookups is very unlikely to be a problem - the cost of execution would sooner limit the performance.

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Runtime.InteropServices;using System.ComponentModel;using System.Threading;namespace ConsoleApplication1{    class Program    {        public static void run(string[] args)        {            Console.Out.WriteLine("Running");        }        public static void print(string[] args)        {            foreach (string s in args) Console.Write(s + " ");            Console.WriteLine();        }        static bool done;        public static void quit(string[] args)        {            done = true;        }        public delegate void CommandDelegate(string[] args);        class Commands        {            private Dictionary<string, CommandDelegate> commands = new Dictionary<string, CommandDelegate>();            public void installCommand(string cmd, CommandDelegate d)            {                commands.Add(cmd.ToUpper(), d);            }            public bool execute(String line)            {                string[] parts = line.Split(' ');                if (parts.Length > 0 && parts[0].Length > 0)                {                    string cmd = parts[0].Substring(1).ToUpper();                    CommandDelegate d;                    if (commands.TryGetValue(cmd, out d))                    {                        d(parts.Skip(1).ToArray());                        return true;                    }                }                return false;            }            public void printHelp(String[] args)            {                Console.WriteLine("Commands:");                foreach (var p in commands) Console.WriteLine(p.Key);            }        }        static void Main(string[] args)        {            Commands cmds = new Commands();            cmds.installCommand("run", new CommandDelegate(run));            cmds.installCommand("print", new CommandDelegate(print));            cmds.installCommand("help", new CommandDelegate(cmds.printHelp));            cmds.installCommand("quit", new CommandDelegate(quit));            Console.WriteLine("Type /help to list commands");            while (!done)            {                string line = Console.ReadLine().Trim();                if (line.StartsWith("/"))                {                    if (!cmds.execute(line)) Console.WriteLine("Invalid command: " + line);                }                else                {                    Console.WriteLine(">" + line);                }            }        }    }}



And, to make it extensible, instead of registering everything up-front, simply pass the Commands object to each subsystem:
class SomeSubSystem {  public SomeSubSystem(Commands c) {    c.installCommand("foo", new CommandDelegate(this.foo));    c.installCommand("bar", new CommandDelegate(this.bar));  }};
Just add an extra check for duplicate commands.
Yea, I am not too worried about the code execute speed of the command process because like you said, no human will be able to type commands fast enough to make it a bottle neck.

I should also mention that I am limited to functionality provided in Mono 1.2.5 so not sure if that means some suggestions here are not possible due to the version of Mono Unity uses.
Quote:Original post by 3dmodelerguy

I should also mention that I am limited to functionality provided in Mono 1.2.5 so not sure if that means some suggestions here are not possible due to the version of Mono Unity uses.


Dictionary and delegates should be available, and that is about all there is to it.
Quote:That is a very verbose, painful and inflexible version of the dictionary suggestion. What exactly do you think your if/else approach offer over a map of strings to functions?


Simplicity. Comparison and dispatch are in a single place, completely exposed to the human eye. It's KISS in purest form. There's no chasing around to find where the dictionary is instantiated and what's in it (probably off with the rest of the process initialization in some other file, great, another window to manage in the editor, like I don't already have 30 of those). I know the current fad is terse code with as much abstraction as possible, but sometimes code is just simpler when it's exposed, stupidly simple and trivially obvious.

I'm a big fan of Big Abstraction for Big tasks. But if it's 5 string commands that simply dispatch to 5 unrelated functions... screw it. It's if/then/else every time. Refactoring for concise coolness can be done if the simple approach ever breaks down - though I'd argue that if there are so many cases that if/then/else doesn't fit in a function anymore, as one fellow mentioned here, then you have too many commands for a human to remember anyway, and your problem is human factors, not code design.

It's worth noting that I currently work for a company where not everyone knows C++, and indeed not everyone is interested in being cool, OO and modern. There can be a lot of advantages to writing the simple code in a simple fashion that *anyone*, from the guys who grew up in Fortran 4 and don't know why we bothered with anything else, to wildest fad follower using the latest language with the niftiest non-procedural construct, can look at and *instantly* understand. It's not worth a day of my time to explain templates, maps, and function pointers to people who will point out, with complete accuracy, that I just buried 10 lines of extremely simple code under a huge, reeking pile of totally unnecessary language coolness.
Quote:Original post by ScottMayo
It's worth noting that I currently work for a company where not everyone knows C++, and indeed not everyone is interested in being cool, OO and modern. There can be a lot of advantages to writing the simple code in a simple fashion that *anyone*, from the guys who grew up in Fortran 4 and don't know why we bothered with anything else, to wildest fad follower using the latest language with the niftiest non-procedural construct, can look at and *instantly* understand. It's not worth a day of my time to explain templates, maps, and function pointers to people who will point out, with complete accuracy, that I just buried 10 lines of extremely simple code under a huge, reeking pile of totally unnecessary language coolness.
That comes across more as a condemnation of the strange creature that is normal C++ code, more than anything else. Don't forget that the OP is working C# - a high level language with a mature container library, and simple delegates, as opposed to the small nightmare that is C++ templates and function pointers.

I would also argue that both the guy stuck in the Fortran era, and the guy who programs in buzzwords, should find stl containers and algorithms to be much easier to comprehend than the basic loops and hand-rolled constructs. If you aren't familiar with the language, a random_shuffle() or partition() is much clearer than the hand-rolled implementation thereof. And even your functional guy should have some LISP/related background in functional programming...

But quite apart from that, it doesn't require 'cool, OO and modern' to recognise where an approach is and isn't efficient, both in terms of performance and man-hours. An in-engine graphical console isn't something you implement on a whim to display the time of day - it requires considerable effort, and you aren't going to throw it away at the end of the week. Thus the if/else chain fails both criteria miserably - maintenance is horrific, and performance is only equivalent to the dictionary approach for a very small number of items. And when the list grows, and either the maintenance cost or the performance becomes unacceptable, you have to rip it out and use a dictionary *anyway*.

Chances are, by the time you are done with all that, you might as well have gone ahead and implemented the scripting language [smile]

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Quote:Original post by ScottMayo
from the guys who grew up in Fortran 4 and don't know why we bothered with anything else


These people are not developing in Unity engine, using C# running on Mono. So whatever their opinion might be, it's not relevant in this thread.
ScottMayo does imo make an interesting point though - maybe not applicable
in every situation but at the very least something to think about.

As an example,
I've got a little debugging webserver where you can register webpages (derived from abstract webpage class), register them at a server-class (derived from a connection/socket-class) which manages connections
and returns the requested pages (stored in a map of webpage*'s) - OO C++ like
anyone would use it.

Needed the same thing in a limited platform (had to go back to C), and honestly,
I went for the KISS approach, killed around 50-60% of the C++ code while rewriting
it to C, using a simple functionpointer-callback at one point, and it still
works.

Admitted, I now have a hardcoded limit for #pages instead of a dynamic container - and that's not because it wouldn't be possible, but rather because I wanted to get something up and running quickly to spend time on other things; it's easy to increase the hardcoded value - and if I really wanted, I could change the structure to be dynamic without changing the interface - *if the need arises*.

The excercise did make me think about the entire "lets abstract it away",
generating huge piles of wrapping/abstraction code, however ^^

ps: I do have to admit that not having constructors is annoying though...
visit my website at www.kalmiya.com
Quote:Original post by Kitt3n
OO C++ like anyone would use it.
Except, apparently, me [smile]

Just because C++ is so often referred to as an OO language, doesn't mean that you can only write OO code. If you write idiomatic C++ using the standard library and boost, it ends up looking a whole lot more like functional programming.
Quote:The excercise did make me think about the entire "lets abstract it away", generating huge piles of wrapping/abstraction code, however ^^
I hate to come out and say it like this, but if abstraction is requiring 'huge piles of code', then you are most likely doing it wrong.

And back to the strange discussion of if/else chains vs a dictionary, can someone please enlighten me as to why they feel that this:
if str == 'option1':	option1();elif str == 'option2':	option2();elif str == 'option3':	option3()else:	raise Exception('unknown option: ' + str);

is preferable to this?
options = {'option1': option1, 'option2': option2, 'option3':option3}options[str]()


[Edited by - swiftcoder on July 21, 2009 5:29:06 PM]

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

This topic is closed to new replies.

Advertisement