Hi all,
I am trying to find a way to cast a value to a union type without the use of a dummy variable. My union definition is something like
typedef union _MY_UNION { unsigned long u32; unsigned int u16; unsigned char u8; signed long s32; signed int s16; signed char s8; float fl; } my_union;
I have discovered that I can assign a value to the union at initialization using a line like
my_union z = { 0 };
which works fine. It assumes the first union member as the type of the assignment, so ends up doing the same thing as
my_union z; z.u32 = 0;
except in a much cleaner fashion.
Now for the tricky part -- I have a function that accepts this union type, i.e.
void do_union(my_union u) { ... }
and I would like to be able to call this function without having to pass through a dummy variable first, i.e.
do_union(z); // works as expected, given the definition of z above do_union(0); // C193 incompatible operand do_union((my_union)0); // C215 illegal type conversion do_union((my_union){0}); // C141 syntax error near '{' (as expected; this is initialization syntax)
I get the exact same errors if I use a variable of any member type (including the first) instead of the constant 0. The only way I have been able to get this to work is to use a dummy union variable, i.e.
unsigned long i; my_union t; [...] t.u32 = i; do_union(t);
Is there any way to get around this, and let me call the function directly without the use of a dummy variable?
Regards, -Scott
"All of this has happened before, and it will all happen again."
Here it is:
http://www.keil.com/forum/16836/
In a nutshell: no, there's no way to do that in a C90 compiler. And IMHO that's a good thing.
In C99 you could do what you want for constants ("compound literals"), but not for variables.
Whatever you intended to do there --- that union is clearly not serving any purpose worth being served. For starters, any such union really should be bound to some indicator to tell the called function which element of the union you're currently using. Usually that's a struct holding that union and an enum as the indicator.
Without that information, the union offers no advantage whatsoever over a plain dumb (void*) or array of unsigned char to transport data of unknown type.
It assumes the first union member as the type of the assignment,
No. That's not an assignment, it's an initialization. Yes, there's a difference.
This is why I am asking the question, because I don't see another way to do this.. the indicator is not necessary, since the function truly does not care what it is being passed; (void*) (from what I can tell, anyway) uses 3 bytes, while data being passed in could be up to 4; array of unsigned char requires a separate buffer with pointer in each calling function (which could just as easily be a my_union variable).
Here are my goals for do_union; if you have any other ideas, or ways you can make the ideas above work, I would be happy to hear them:
'do_union' should:
- be intrinsically re-entrant (use only registers) - accept 32-bits, with no concern for what those bits meant - be easily callable from other routines, allowing them to pass whatever 4-byte value they wish
Assume that R1-R3 are already being used by a (void *).
In this context, not so much that you can't tell what I meant. From my reading, it assumes the first union member as the type of the initialization.
But wait a minute.
The compiler may already have such a magic construct. I think it is sometimes called unsigned. For people who want to be a bit more fancy pants, they may be called uint32_t (after inclusion of stdint.h).
The receiving end can pick up this 32-bit general-purpose value. Store it in a register and optionally transmit it further. The code can perform bit operations or use the parameter for strchr() calls or enumerators or a large number of other things. The language standard have allready catered for the ability to call functions with const values, without the need for initializing any temporary variables. They are atomic for most 32-bit processors (unless possibly with 8- or 16-bit memory subsystems). They have very good support for code optimization - many compilers specifically have the ability to track the usage pattern and sometimes move the contents from memory to registers, and sometimes from registers to memory. And to analyze life span to let multiple such magic containers share the same register and/or memory region in different parts of a function.
The language standard does describe rules for initializing such a magic construct with signed or unsigned values of smaller size. Many 32-bit architectures also manages to efficiently and sometimes totally transparently transpose these magic containser between integer form and pointer form. Oh joy, to store 0 to the container or to be able to test if (!magic_container) without first having to figured out if the magic container is expected to store a pointer or a numeric entity.
I'm not sure if someone may have any patents on the "unsigned" construct, but I haven't seen any royalty requirements, so I can't see why it shouldn't be presented for a larger audience as a very valuable tool. Some people might even consider it a basic building block - like the characters in our alphabet.
the indicator is not necessary, since the function truly does not care what it is being passed;
Then the union is a waste of effort.
(void*) (from what I can tell, anyway) uses 3 bytes,
Yes --- but that doesn't have anything to do with what you're trying to do here.
Have you ever heard of functions like memcpy(), or memcmp()? Why, do you think, is their argument of type (void *)? Why do you think the argument of the Standard C Library's generic "write whatever this is to a file" function, fwrite(), takes its argument as a (void *)?
- be intrinsically re-entrant (use only registers)
No such thing in C51. You may get lucky for a while --- but there's really _no_ being sure.
Other than that, either (void *) or (unsigned char *) is really what you need:
do_something(&variable);
Sorry -- I thought you were referring to casting the variable itself as a (void *).
This [1] is disconcerting then, since it implies that this process is deterministic.
Sure I have, but they are written to be as generic as possible. I was trying to use some of the specificity I have available to simplify things a bit. By passing a pointer to the variable, I am necessitating that the user of the function have a variable in which their data is stored; using this method, they couldn't do something like:
do_something(load(i)+x);
but would instead need to do something like
y = load(i) + x; do_something(&y);
Certainly this is not the end of the world, I was just trying to make it an easier function to use.
If this is the extent to what I can do with C(x)51, then that answers my question -- you can't do it without a dummy variable. I already have several solutions (that are more appropriate for my particular code) that use these dummy variables.
--- [1] http://www.keil.com/support/man/docs/c51/c51_ap_parampassreg.htm