What do you guys think is a good way to deal with this situation:

#define TYPE_INT   0  // 4 byte, signed
#define TYPE_CHAR  1  // 1 byte, signed
#define TYPE_UINT  2  // 4 byte, unsigned
#define TYPE_FLOAT 3  // 4 byte, signed, floating point

typedef struct {
  float Value; // This value can be of any type, but initially stored in a float
  int   Type;
} AnyVal;

void main() {

   // The value is loaded by some function, it can have a value of any type in it
   AnyVal *Val = LoadVal();

   // Now do some math with that value
   switch (AnyVal->Type[0]) {
     case TYPE_INT:  ((int)(AnyVal->Value)) /= 2; break;
     case TYPE_UINT: ((unsigned int)(AnyVal->Value)) /= 2; break;
     case TYPE_CHAR: ((char)(AnyVal->Value)) /= 2; break;
     case TYPE_FLOAT: AnyVal->Value /= 2; break;
   }
   
   // Now send it back with the math done
   ValContinueProcessing(Val);

}

Basically I have a structure that can hold a value which can be one a several types, and that type is stored in an integer defined by a series of macro's. While this code seems kind of useless in this context, it has a significant meaning to the code I am working on. The problem is that doing math on this value requires checking for the type and performing the same math with the only difference being the type name.

Is there a way I could do something like this instead?

switch (AnyVal->Type[0]) {
     case TYPE_INT:  MAGICTYPE = int; break;
     case TYPE_UINT: MAGICTYPE = unsigned int; break;
     case TYPE_CHAR: MAGICTYPE = char; break;
     case TYPE_FLOAT: MAGICTYPE = float; break;
   }
   ((MAGICTYPE)(AnyVal->Value)) /= 2;

...Or use a macro or something? Problem with macro's is that it resolves at compile time, and this needs to be done at runtime.

I would much rather write the math once and create a switch for the type, instead of copying the math over and over just to change the type name. In my application, the math is quite complex, and I am having to repeat it more times than I would like, but I can't think of a better solution.

And as much as I cringe at the thought of using compiler extensions, id use it if it can solve this problem. I am using the gcc compiler, latest build.

Recommended Answers

All 7 Replies

It might be safer to use a union, such as VARIANT.Your structure does not have to be that complicated

typedef struct
{
   int type;
   union {
     char cVal;
     int iVal;
     float fVal;
   } val;
     
}AnyVal;

int main()
{
    AnyVal val;
    val.type = TYPE_FLOAT
    val.val.fVal = 0.0F;
}

<nevermind>

Are you sure unions will work well with types of different sizes? Char is 1 byte where everything else is 4 bytes.

And how would you suggest I perform the math so that no matter what type it is, it will still result in all types having the same result value?

Is val.val /= 2 legal? Or if not, should I just do all the math with the float and it will safely perform the math to all values, even if its the char?

>>Are you sure unions will work well with types of different sizes?
Absolutely yes. That's the purpose of unions. The sizeof(AnyType) is the size of its largest member.

>>Is val.val /= 2 legal
No, but val.val.iVal/2 is legal

int result = 0;
switch(val.type)
{
   case TYPE_INT: result = val.val.iVal/2; break;
   case TYPE_CHAR: result = (int)val.val.cVal/2; break;
   case TYPE_FLOAT: result = (int)val.val.fVal/2; break;
}

Ok, so I was going to apply this union technique to my program and realized it was not entirely compatible with my specific situation, so I messed up with my question.

In my app, its actually a POINTER to a value whose type is recorded in the struct.

So this leads to a more important question...

char   A = 10;
float *B = (float *)&A;
B[0] /= 3;

Will this create a suttle memory overrun? I would think that if A was 9 there would be no problems, but with the answer being 3.33333~, isnt that automatically a 4 byte number? Will it eat up 3 bytes of memory that does not exist?

In this case I would probably have to copy the value into a 4 byte float and do the math, then cast it to char and copy it back to the value pointed to by the val pointer.

Am I right worrying about that kind of math?

>>Will this create a suttle memory overrun

Absolutely. sizeof(A) is 1. sizeof(float) is 4. Now you guess the result.

>>B[0] /= 3l

Wrong. It's *B /= 3; because B is NOT a pointer to an array but a pointer to a float. But since A is not a float that code snippet will have dedefined behavior, possibly crashing the program.

@Acient Dragon

I just want to make a correction to something you said. In C, all variables can be treated as an array of one element. This is because all variables are pointers to memory locations, and there is literally no difference between that and arrays whose name simply points to the first element of its array.

Since in my example B is a pointer to float, the compiler knows that B is an array of one element in which each element is 4 bytes. So technically, the compiler simplifies B[0] to mean *(B + (0*4)), which simplified further means *B.

If I had said B[1], it would be a problem because *(B + (1*4)) points immediately after the allocated memory of the variable, so its a random memory space.

I like addressing my pointers as arrays of one element because I find floating asterisks confusing, especially when I am doing math on the value that the pointer points to.

Anyways, I created a work around for my problem, which is to simply copy the value to a temp float, do the math, then copy it back to the value casted by type.


P.S. Some of my most hardened enterprise level software which I developed with serious uptime requirements involve code entirely written in this fashion. They never crash or have unpredictable behaviour.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.