2

This is a small C++ application (and the table is not huge either) so if possible I would like to avoid include anything other than STL (and boost).

So to the problem:

I have a table of predefined matching of input and output conditions. This table contains 4 input variables (states), and their combination leads to 1 output variable. In essence:

output = f(a,b,c,d) 

i.e. "the output variable is a function of 4 input variables"

  • The output variable is an alarm and can take one of 3 states (ok,warning,fault).
  • "A" can take 1 of 3 states (say 0 or 1 or 2). Similarly
  • "B" can take 1 of 5 states
  • "C" can take 1 of 3 states
  • "D" can take 1 of 2 states

I want to provide the values to the input variables and get the output variable in return from this lookup table.

The table may look a bit like this:

A | B | C | D | output  
----------------------
1 | 3 | 0 | 1 | ok
0 | 2 | 1 | 2 | fault
0 | 0 | 0 | 0 | warning

... and so on. So the table will have no more than 150 entries (some optimisation has helped in this) which is not huge, but it is still too large if I try to create nested if-statments. What can I do to avoid huge if statements?

What I am looking for is a design pattern , or some good reference to read up on.

For example a sort of map which can hold 4 keys and 1 value would be ideal. Something like map would probably be ideal.

1
  • Can you concatenate the 4 variables into a single (probably string) key? Using your first example, make a key "1|3|0|1" and a value "ok" Jun 30, 2016 at 18:53

2 Answers 2

4

Compile time solution 1 (clean)

You could "concatenate" your input values using tuples, and map these tuples to your output type. Here how to do it with a string as output:

string f (int a, int b, int c, int d) {
    using input =  tuple<int,int,int,int>;
    
    static map<input,string> mytable =  {
        { input{ 1, 3, 0, 1 }, "ok"}, 
        { input{ 0, 2, 1, 2 }, "fault"},
        { input{ 0, 0, 0, 0 }, "warning"}
    };
    return mytable[input{a,b,c,d}];
}

Here an online demo. Note that in the snippet above, if the combination doesn't exit a new combination is added to the map with an empty string. If you'd need to handle such errors, you could for example use mytable.at(...) instead of mytable[...] to throw an out of range exception

This approach is very powerful, because the tuples can aggregate the real types that your using for your input data. It's easy to maintain. And in addition the map search is very efficient: O(log n) compared to O(n) for an if-chain.

Compile time solution 2 (easy)

Another approach, could be to combine the states, if each has a numeric value. This is not so clean as the previous one ecause of the strong assumption on the input type, but it could do the trick:

string f (int a, int b, int c, int d) {
    // assumes that a,b,c and d are between 0 and 9 

    static map<int,string> mytable =  {
        { 1301, "ok"}, 
        { 0212, "fault"},
        { 0000, "warning"}
    };
    return mytable[a*1000+b*100+c*10+d];
}

Note that you could also use a switch statement in this case. Most of the modern compiler generate an optimiszed branch table, which will in any case be at least as performant than an if-chain if not more.

Run time approach (flexible)

A variant of the solution 1 could be to initialize the map at runtime. Instead of specifying an initializer list in the code, you'd just read once the input and output data from a file and build the map during your application startup.

May be it's not relevant for your problem. But I wanted to mention it here, because this may sometimes help to get a very flexible software that lets user choose the desired behavior at a minimal cost.

2
  • 2
    Just a language-specific reminder: some languages interpret integer literals that begin with zero as octal (base-8). Unfortunately this weird interpretation affects quite a few languages that are considered high-level and modern.
    – rwong
    Jun 30, 2016 at 21:31
  • @rwong Yes ! You're completely right and thanks to pointing it out ! The leading zeros should be removed. I'll leave them in the answer just to illustrate the principle (Another variant could be to use a 0x prefix and shift by 12,8,4 instead of multiplying by 1000,100,10)
    – Christophe
    Jun 30, 2016 at 21:56
0

This is a basic mathematical function that translates one domain into another. The suggestions given by previous members are excellent. I would keep the integer as a key, as it requires less space. The hexadecimal approach given by Cristophe might be the best solution here, as it provides you with an easy way of getting the individual inputs back if needed. Do not forget, in case you do not have outputs for all combinations of inputs, make sure you handle the default output properly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.