Looking for the most elegant code dispatcher


Question

I think the problem is pretty common. You have some input string, and have to call a function depending on the content of the string. Something like a switch() for strings. Think of command line options.

Currently I am using:

using std::string;

void Myclass::dispatch(string cmd, string args) {
    if (cmd == "foo")
        cmd_foo(args);
    else if (cmd == "bar")
        cmd_bar(args);
    else if ...
        ...
    else
       cmd_default(args);
}

void Myclass::cmd_foo(string args) {
...
}

void Myclass::cmd_bar(string args) {
...
}

and in the header

class Myclass {
    void cmd_bar(string args);
    void cmd_foo(string args);
}

So every foo and bar I have to repeat four (4!) times. I know I can feed the function pointers and strings to an static array before and do the dispatching in a loop, saving some if...else lines. But is there some macro trickery (or preprocessor abuse, depending on the POV), which makes is possible to somehow define the function and at the same time have it update the array automagically? So I would have to write it only twice, or possibly once if used inline?

I am looking for a solution in C or C++.

1
7
8/19/2009 4:36:31 PM

Accepted Answer

The ugly macro solution, which you kind-of asked for. Note that it doesn't automatically register, but it does keep some things synchronized, and also will cause compile errors if you only add to mappings, and not the function in the source file.

Mappings.h:

// Note: no fileguard
// The first is  the text string of the command, 
// the second is the function to be called, 
// the third is the description.
UGLY_SUCKER( "foo", cmd_foo, "Utilize foo." );
UGLY_SUCKER( "bar", cmd_bar, "Turn on bar." );

Parser.h:

class Myclass {
...
protected:
    // The command functions
    #define UGLY_SUCKER( a, b, c ) void b( args )
    #include Mappings.h
    #undef UGLY_SUCKER
};

Parser.cpp:

void Myclass::dispatch(string cmd, string args) {
    if (cmd == "")
        // handle empty case
#define UGLY_SUCKER( a, b, c ) else if (cmd == a) b( args )
#include Mappings.h
#undef UGLY_SUCKER
    else
       cmd_default(args);
}

void Myclass::printOptions() {
#define UGLY_SUCKER( a, b, c ) std::cout << a << \t << c << std::endl
#include Mappings.h
#undef UGLY_SUCKER
}

void Myclass::cmd_foo(string args) {
...
}
1
8/19/2009 4:47:48 PM

It sounds like you're looking for the Command pattern

Something like this:

Create a map like this

std::map<std::string, Command*> myMap;

then just use your key to execute the command like this....

std::map<std::string, Command*>::iterator it = myMap.find(str);
if( it != myMap.end() ) {
    it->second->execute()
}

To register your commands you just do this

myMap["foo"] = new CommandFoo("someArgument");
myMap["bar"] = new CommandBar("anotherArgument");

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon