0

Presume this situation:

  • Max Number of 256 key slots.
  • Key slots are defined by a struct, and a variable for each (256) has to exist.
  • User defines which keys slots are active at initiation (in my example, I just coded them in). They however will remain constant throughout execution.
  • Program receives input, sends it to slot, and performs user defined action.

Both samples of code presume this struct:

struct Key;
QList<Key*> keyList;
struct Key {
    const uint_fast8_t keyNumber; // Unsigned 8 bit integer
    const QString      action;

    /* Constructor - Will add self to keyList if cmd is specified */
    Key(const uint_fast8_t keyNum, const QString cmd="")
        : keyNumber(keyNum),
          action(cmd)
    {
        if (!cmd.isEmpty) { keyList.append(this); }
    }
};

int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
        uint_fast8_t inputKey;

        /* All variables have to exist. */
        Key _001(1);
        Key _002(2);
        Key _003(3,  "boat programming"); // Automatically added to keyList
        Key _004(4);
        ...
        Key _075(75);
        Key _076(76, "foo bar");
        Key _077(77);
        ...
        Key _254(254);
        Key _255(255,"take yee flask");

        /* etc... */

Solution 1: A giant switch

switch (inputKey) {
    case _001.number: execute(_001.action); break;
    case _002.number: execute(_002.action); break;
    case _003.number: execute(_003.action); break;
    ... 
    ... 
    ... 
    case _255.number: execute(_255.action); break;
    case _256.number: execute(_256.action); break;
    case default: break;
}

Issue: Inefficient if only three actions are defined

Solution 2: A for loop on a list generated at initiation.

for (int i=0; i<keyList.length; i++) {
    if (inputKey == keyList.at(i).keyNumber) {
        execute(keyList.at(i).action);
        break;
    }
}

Issue: Progressively less efficient compared to switch statement as more items are added.

Solution 3: Is this possible?

switch (inputKey) {
    case _003.number: execute(_003.action); break;
    case _076.number: execute(_076.action); break;
    case _255.number: execute(_255.action); break;
    case default: break;
}

Am I right on the track, or should I be approaching this situation differently?

Obviously my goal is to have the optimal performance within obvious discretion:

enter image description here

Anon
  • 3,639

1 Answers1

5

In C++11 or better you could consider using a map of lambdas, e.g.

 std::map<unsigned, std::function<void(void)> actmap;

You'll fill it dynamically, e.g.

unsigned num = somenumber();
actmap[num] = [=](void) { /* some code for the lambda */ };

BTW since you have only 256 possible numbers, you might just use the (probably faster) std::array<std::function<void(void),256> actmap;

then, given some num number, use

 actmap[num]();

to run that action.

If you use GCC as your compiler and accepts non-standard extensions consider -alternatively- using computed goto-s (to labels starting some block).

You could also use some JIT compiling meta-programming approach: From the initial data you'll generate (executable) code at runtime using some JIT library like libgccjit or LLVM or asmjit etc. A variant would be to generate some temporary file containing some emitted C++ code, compile it (by forking a compiler) into a plugin, and use dlopen etc... to load that generated plugin. See also this and that.

Regarding performance, try several ways and measure i.e. benchmark them (don't forget to enable optimizations in your compiler), and choose the best. Perhaps the dynamic call overhead (and the closure making) might not be that important.