views:

131

answers:

6

I'm working with HiTech PICC32 on the PIC32MX series of microprocessors, but I think this question is general enough for anyone knowledgable in C. (This is almost equivalent to C90, with sizeof(int) = sizeof(long) = sizeof(float) = 4.)

Let's say I read a 4-byte word of data that represents a float. I can quickly convert it to its actual float value with:

#define FLOAT_FROM_WORD(WORD_VALUE) (*((float*) &(WORD_VALUE)))

But this only works for lvalues. I can't, for example, use this on a function return value like:

FLOAT_FROM_WORD(eeprom_read_word(addr));

Is there a short and sweet way to do this inline, i.e. without a function call or temp variable? To be honest, there's no HUGE reason for me to avoid a function call or extra var, but it's bugging me. There must be a way I'm missing.

Added: I didn't realise that WORD was actually a common typedef. I've changed the name of the macro argument to avoid confusion.

+8  A: 

You can run the trick the other way for return values

float fl;
*(int*)&fl = eeprom_read_word(addr);

or

#define WORD_TO_FLOAT(f)  (*(int*)&(f))

WORD_TO_FLOAT(fl) = eeprom_read_word(addr);

or as R Samuel Klatchko suggests

#define  ASTYPE(type, val) (*(type*)&(val))
ASTYPE(WORD,fl) = eeprom_read_word(addr);
John Knoeller
R Samuel Klatchko
Good suggestion, I'll add that to the answer
John Knoeller
Quick and simple, I like it. Cheers.
detly
make sure the endianness works out correctly with your read/write routines for the eeprom :)
Mark
A: 

Not really an answer, more a suggestion. Your FLOAT_FROM_WORD macro will be more natural to use and more flexible if it doesn't have a ; at the end

#define FLOAT_FROM_WORD(w) (*(float*)&(w))

fl = FLOAT_FROM_WORD(wd);
John Knoeller
Silly typo, fixed :)
detly
A: 

It may not be possible in your exact situation, but upgrading to a C99 compiler would solve your problem too.

C99 has inline functions which, while acting like normal functions in parameters and return values, get improved efficiency in exactly this case with none of the drawbacks of macros.

Roger Pate
HiTech don't make a C99 compiler, so... no can do :/
detly
A: 

This is caused because you cant take the address of temporary objects, as they are about to be deleted. That is why this is also not possible:

int a=1;
int b=1;
int& c = &( a + b ); 

So short answer: not really.

You could make a macro that takes a float and sets it to the expression passed:

#define FLOAT_FROM_WORD(f, w) WORD w32 = w;\
f = (*(float*)&(w32))

main()
{
    float f;
    FLOAT_FROM_WORD( f, eeprom_read_word(addr) );
}
0xC0DEFACE
One recommendation is to wrap your macro in a do/while(0). This way the macro variable will have it's own scope so you don't have to worry about name conflicts with your temporary variable `#define FLOAT_FROM_WORD(f, w) do { WORD w32 = (w); f = *(float *) } while (0)`
R Samuel Klatchko
do you need to bother with a do while? I thought you could just put stuff in curly brackets and it will create a new scope?FLOAT_FROM_WORD(f, w) { WORD w32 = (w); f = *(float *) } is this only a c++/visual c++ thing?
matt
@R Samuel - The `do { ... } while(0)` trick prevents the macro from "returning a value," which 0xC0DEFACE has already given up in favor or a more general solution so there is no overall harm in this case.
Chris Lutz
good point with the naming conflicts. One more reason I don't use macros :P
0xC0DEFACE
@matt - Yes. If you just wrap it in brackets, bad things happen for `if(x) FLOAT_FROM_WORD(f, w); else do_stuff();` which expands to `if(x) { ... }; else do_stuff();` The `;` is an empty statement, which prevents the `else` from binding to the `if` clause and causes a syntax error. `do { ... } while(0)` allows the macro to be used fully as if it were a function call, rather than causing corner-case bugs like this.
Chris Lutz
@0xC0DEFACE - You can avoid naming conflicts by using macro text-joining tools to join a temporary prefix name and the `__LINE__` macro, creating unique identifiers for each line that are unlikely to conflict with already in-use identifiers. Though the necessity of this in smaller macros is arguable.
Chris Lutz
+1  A: 

you can take the address of a temporary value if you use a const reference:

FLOAT_FROM_WORD(w) (*(float*)&(const WORD &)(w))

but that won't work in c :(

(c doesn't have references right? works in visual c++)

as others have said, be it an inlined function or a temp in a define, the compiler will optimize it out.

matt
+3  A: 

If this were GCC, you could do this:

#define atob(original, newtype) \
  (((union { typeof(original) i; newtype j })(original)).k)

Wow. Hideous. But the usage is nice:

int i = 0xdeadbeef;
float f = atob(i, float);

I bet your compiler doesn't support either the typeof operator nor the union casting that GCC does, since neither are standard behavior, but in the off-chance that your compiler can do union casting, that is your answer. Modified not to use typeof:

#define atob(original, origtype newtype) \
  (((union { origtype i; newtype j })(original)).k)

int i = 0xdeadbeef;
float f = atob(i, int, float);

Of course, this ignores the issue of what happens when you use two types of different sizes, but is closer to "what you want," i.e. a simple macro filter that returns a value, instead of taking an extra parameter. The extra parameters this version takes are just for generality.

If your compiler doesn't support union casting, which is a neat but non-portable trick, then there is no way to do this the "way you want it," and the other answers have already got it.

Chris Lutz
The sad thing is, Microchip actually make a GCC based compiler and they've technically released the source. However, I've never been able to coax it to compile on Linux.
detly