views:

310

answers:

6

I have a program that needs to do a *compile time checkable** map from one known set of values to another known set of values:

in      out
------------
8       37
10      61
12      92
13 1/4  109
15 1/4  151
etc

This would be easy if the inputs were either integers or evenly spaced. I'm going to be iterating over the rows but also want to be able to do lookups in a readable manor.

My current thought (that I'm not liking) is to define an enum like

enum Size
{
   _8,
   _10,
   _12,
   _13_25,
   _15_25,
   // etc
}

and then set it up for 2 lookups.

Any better ideas?

Edit: My primary concern is limiting what I can try to look up. I'd like stuff to not even compile if the code might try and look up something that is invalid.

The set is small and iteration times are almost totally irrelevant.

I haven't seen anything that gains me anything over the enum so for now I'm going with that. OTOH I'll keep watching this question.

* Note: I'm not worried about catching issues with pointers and what not, just straight forward code like for loops and variable assignments.


The nitty grity: I over simplified the above for clarity and generality. I actually have a table that has 3 non-integer, non-uniform axes and one non-numeric axis. And at this point I'm not sure what directions I'm going to need to enumerate it in.

a few links to give a flavor of what I'm looking for:

Boost::SI and my D version of the same idea

A: 

Sounds like you want to want to use something like a sorted binary tree. Both lookup and iteration are fast and the tree won't care about the spacing of the entries.

If your multiple axes are independent, you could create one for each axis.

David Norman
it's not a DB like situation. It's almost pure numeric code.
BCS
+1  A: 

Can't you use a hash map?

Draemon
a hash won't stop me from trying to look up an invalid value.
BCS
Huh? That constraint was not in the original question. If you need, keep a list of "good" values and check it prior to the map.
Tim
See my edit/clarification.
BCS
BCS: Any hashmap will tell you whether a value is contained or not.
Aaron Digulla
not at compile time, every hash map I've seen will let you *try* to look up something invalid
BCS
Note for the downvoter: There was no mention of compile-time restrictions on lookups when I posted this question. Hash-Maps are a perfectly valid suggestion.@BCS: Yes, but that wasn't mentioned in your original question.
Draemon
BTW, I Up-voted you b/c your answer addresses some of my question and is a good answer for related situation. (down voting for partial answers is bad, they might suggest better answerers)
BCS
Fair enough. I doubt you're going to find anything better than enums for your situation. If you want it checked at compile time you're limited to enums and defines. Obviously enums are better in this case.
Draemon
A: 

The enum idea wasn't too terrible but I would do it dynamically. You have an array/list of valid strings. The index into the list of strings is your key to your map.

// this could be loaded from a file potentially
// notice that the keys have been sorted.
const char* keys[] = { "10", "12", "13 1/4", "15 1/4", "8", 0 };
float values[] = { 61, 92, 109, 151, 37, 0 };
int key_count = 0;
while (keys[key_count]) ++key_count;

bool find(const char* key, float* val) {
   int idx = bsearch(key, keys, sizeof(const char*), key_count, strcmp);
   if (idx < 0) return false;
   *val = values[idx];
   return true;
}

Now, you said something about there being more than one dimension here. That just means you need multiple keys arrays.

jmucchiello
+1  A: 

If your input fractions are limited to some power-of-2 denominator, you could use fixed point numbers as Keys. For your example case, use 1 bit = 0.25, (multiply each input by 4) like so:

IN maps to Key
--         ---   
8          32   
10         40
12         48 
13 1/4     53
15 1/4     61 

KeyMin= 32

Then you can use Key-KeyMin as the index into a sparse array which contains a flag value like -1 for the invalid entries. The advantage is that it saves you from having to recode if your keys change. The disadvantage is wasted memory.

AShelly
a good partial solution (+1), But I really rather be able to do a compile time check. I'm starting to think that there will be no good solution (at least in existing C like languages)
BCS
AShelly
If I have a complete map from an enumeration to a set of values, than as long as I keep stuff simple (no pointers, casts, math, etc.) then the type system will make sure that I can't use an invalid value as a key.
BCS
A: 

Here is a suggestion to how you could solve it. Using structs and arrays.

typedef struct{
    float input;
    int   output;
}m_lookup;
m_lookup in_out[] = 
{   
    (float) 8   , 37,
    (float)10   , 61,
    (float)12   , 92,
    (float)13.25,109,
    (float)15.25,151,
};

int get_Var(float input)
{
    int i=0;
    for(i=0;i<sizeof(in_out);i++)
     if(in_out[i].input == input)
      return in_out[i].output;
    // Here you could make some special code for your compiler
    return 0;
}
int main(void)
{
    printf("Input 15.25 : Output %d\n",get_Var(15.25));
    printf("Input 13,25 : Output %d\n",get_Var(13.25));
    printf("Illegal input:\n");
    printf("Input 5 : Output %d\n",get_Var(5));
    system( "pause" );
    return 0;
}

I could probably make some sdjustments if you explain a little more about the The nitty grity.

If you are determend to get it checked at compile time then you can use the enum like this:

enum Size
{
   i_8=37,
   i_10=61,
   i_12=92,
   i_13_25=109,
   i_15_25=151,
   // etc
}
eaanon01
It still doesn't solve the problem I'm looking at that I can still code something that will compile but is wrong (e.i. no matching value)
BCS
I updated the answer with a posible solution to use so that a nonexsistant value will fail att compile time
eaanon01
adding the enum, I might as well go for a packed array rather than a hash and I'm back to where I started.
BCS
+1  A: 

Using enums you lose the numeric value unless you do an ugly parse of the variable name. I would do this:

class Size
{
    public decimal Val{get;set;}
    private Size(decimal val){this.val = val;}
    public static Size _8 = new Size(8.0);   
    //...
    public Dictionary<Size, Size> sizeMap = new Dictionary<Size, Size>
    {
        {_8, _37}, 
        //...
    };
}
RossFabricant