2

I have the following problem.

In the input consol I can input a string, and the system will take action based on such.

So if I input add_2_with_2, it will give me a 4, if I input sqrt_4 it will give me 2, etc. Generally, you will do this with a switch / case command, but the problem is, then I need a condition for each case. So if i want to ADITTIONALLY input cube_2, then I have to write a case for this.

However, I would like to do the same without having to explicitly write a case each time I insert a new command. So forexample, if in input "FUNCTION_1", then the program should look in a specific place, in a specific forlder / file, find out if the function is defined, and execute it. If not defined in the file / folder, then it should throw AN EXCEPTION. If I additionally want to input "FUNCTION_2", then i will define the function in the same file or folder (whatever is possible with D) and then let the original program to automatically search and execute.

Can this be done in D?

(sorry for stupid question and bad English)

DejanLekic
  • 18,787
  • 4
  • 46
  • 77
Sean
  • 789
  • 6
  • 26

3 Answers3

3

As it happens, I just did something like this:

https://github.com/schancel/gameserver/blob/master/source/client/messaging.d

Code isn't the prettiest, but it uses reflections to insert the extra case statements.

3

Yes, you can, there's a few ways you can do it.

1) You can call functions from inside the one program, and automatically find/map them using compile time reflection.

I did this in my terminal emulator's utility program. See the source to see how I did it: https://github.com/adamdruppe/terminal-emulator/blob/master/utility.d

To use it for your own purpose, you can remove the version() statements, change the module name, and write your own functions.

2) You can also look for a script in a directory and run them that way. Use std.process and std.file to find a file and run it.

Adam D. Ruppe
  • 25,382
  • 4
  • 41
  • 60
3

What I believe you are looking for is generally called in the literature as the Command Pattern. In heavily OO languages this pattern typically involves creating bunch of classes that implement a common, simple Command interface which has a single execute() method. In D however, you have delegates and can probably avoid generating potentially hundred small classes for this purpose.

Here is one of the possible D alternatives, using lambda expressions ( http://dlang.org/expression.html#Lambda ):

module command2;

import std.stdio;
import std.conv;
import std.array;

// 2 = binary operation
alias int delegate(int arg1, int arg2) Command2; 

// Global AA to hold all commands
Command2[string] commands;

// WARNING: assumes perfect string as input!!
void execute(string arg) {
    auto pieces = split(arg);
    int first = to!int(pieces[1]);
    int second = to!int(pieces[2]);
    Command2 cmd = commands[pieces[0]];

    int result = cmd(first, second); // notice we do not need a big switch here
    writeln(arg, " --> ", result);
} // execute() function

void main(string[] args) {
    commands["add"] = (int a, int b) => a + b;
    commands["sub"] = (int a, int b) => a - b;
    commands["sqrt"] = (int a, int b) => a * a; // second parameter ignored
    // ... add more commands (or better call them operations) here...

    execute("add 2 2");
    execute("sqrt 4 0"); // had to have 0 here because execute assumes perfect imput
} // main() function

Here is the source code to fork and play with: http://dpaste.dzfl.pl/41d72036

I will write the OO version when I find more time...

Regarding the execution of a script/application in some directory... It is merely a matter of writing a function which takes parameters, and calls the std.process.execute(). A very quick example how to extend the code above:

// WARNING: no error checking, etc!
int factoriel(int arg, int ignored) {
    auto p = std.process.execute(["./funcs/factoriel", to!string(arg)]);
    return to!int(p.output);
} // factoriel() function

...
// in main()
commands["fact"] = toDelegate(&factoriel);
...
execute("fact 6 0"); // again, we add 0 because we do not know how to do unary operations, yet. :)
DejanLekic
  • 18,787
  • 4
  • 46
  • 77
  • You could at least vote for the answer, or accept it, as it answers your question... :) – DejanLekic Nov 08 '13 at 09:56
  • voted...... it describes my problem, but the i prefer the other answer as it is more detail of a solution – Sean Nov 08 '13 at 10:24
  • No matter how much I respect Adam and his work (and I really do!), he does not implement the command-pattern in that example, he simply follows the basic design principles, and calls a method/function inside a case block to make the switch block as short as possible. Command Pattern literally removes the switch entirely... Rule of thumb - whenever you see a big switch, it is a good idea to refactor it. If I find some time, I will give you an example how to accomplish what you need for that project you work on... :) – DejanLekic Nov 08 '13 at 10:33