|
Doing it Wrong: Unions
Last post 08-28-2008 9:50 PM by tgape. 44 replies.
-
08-27-2008 11:37 AM
|
|
-
DemonWasp


- Joined on 08-27-2008
- Posts 4
|
While hunting down the source of some unexpected behaviour, I ran across something that made me do a double-take. First, however, the background: The project in question is very young (less than a year old at this point, and this was a product that will probably exist for at least the next half-decade, if not decade. My position in the group was as a temporary employee in a junior position; I was writing a test suite (necessary, but it's often booooring). Anyway, the WTF class looks like the following (heavily anonymised C++):
class Response {
public:
Response() {}
~Response() {}
StatsResponse& getStatsResponse() { return _statsResponse; }
void setStatsResponse ( StatsResponse& statsResponse )
{
_statsResponse.setFieldOne ( statsresponse.getFieldOne() );
_statsResponse.setFieldTwo ( statsresponse.getFieldTwo() );
_statsResponse.setFieldThree ( statsresponse.getFieldThree() );
_statsResponse.setFieldFour ( statsresponse.getFieldFour() );
}
private:
union {
StatsResponse _statsResponse;
};
}; If you, like me, have no idea what "union" implies, I'll save you the googling: a union groups a bunch of variables in the same way that you would expect a struct { } to, except that they all start at the same memory address. This means that when you write values to one variable, you write them to all others. Apparently this was used (and is still used) in things like YACC as a sort of poor-man's object-oriented capacity, since you can return any one of several different types. Note: the four fields shown are all the fields in StatsResponse.
There are no missing entries to that union. This is not example code,
or something that is going to be changed: this has been pointed out
here as a WTF but ignored (apparently this was coded by someone of importance, and not to be changed). To summarise the WTFs:
- _statsResponse can be accessed exactly like a private member field.
- _statsResponse is union'd with nothing else, so it's EXACTLY like a private member field.
- union isn't even useful here, since we're coding in C++, so we have proper OO (or at least, as proper as C++ gets)
- using union in the first place
- StatsRespone cannot have a constructor or destructor, as these are not supported within unions. Since it just happens to hold ints in this case (at this time), that's not a problem...yet. If the class ever has to hold some other object, it'll have to be careful about it.
Also, TRWTF: I can't find any easy way of posting properly-indented code (at least, nothing looks correct in the Preview tab). I can only hope it at least got line-breaks right; apologies if it doesn't.
|
|
-
-
DogmaBites


- Joined on 07-15-2008
- Posts 47
|
Re: Doing it Wrong: Unions
Unions made a lot more sense in plain C, where there was no OO. In that case, you would have something like: struct { int type union { int i; char * s; // etc } } There 'type' would tell you what was stored. With base and derived classes you can treat types abstractly if neccessary. It looks like an historical leftover (even though the project is young) mixed with some cargo-cult programming. At one time there may have been multiple members in the union, but remvoed at a later date. Whoever combined them into one left the union statement. Either through negligence or worrying about touching something that worked.
|
|
-
-
DemonWasp


- Joined on 08-27-2008
- Posts 4
|
Re: Doing it Wrong: Unions
I don't doubt that they made more sense with C, but this is C++ - the entire project. Unless this is copypasta, it's all new, hand-written code. Since you suggested it might have been modified at some point, I checked - the code I posted is the only revision, so stuff could only have been removed if this was a copy-paste. It's not like this hasn't been pointed out: everyone who's seen is has said "wtf" in response, but it remains uncorrected (perhaps the larger WTF than it existing in the first place). I'd remove it myself if I were both still there, and I could justify changing something that the more senior members of the team didn't (I suspect I don't know the full story about why it's still there).
|
|
-
-
DogmaBites


- Joined on 07-15-2008
- Posts 47
|
Re: Doing it Wrong: Unions
I didn't think about copy and paste, but that makes more sense. At least I hope it's C&P. I would worry about someone who put it in there intentionally. I sometimes think we give too much deference to seniority. It's one thing if you are not sure about the problem. It's quite another when it's clearly wrong. However, you now have the best excuse in the world!
|
|
-
-
morbiuswilters


- Joined on 01-15-2008
- Cambridge, MA
- Posts 2,860
|
Re: Doing it Wrong: Unions
DogmaBites:Unions made a lot more sense in plain C, where there was no OO.
C wasn't a strongly object-oriented language, but it definitely had OO abilities.
< pstorer> Bans don't mean shit on the forum. It's like being on the Sex Offender List. You can still entice kids into your van with candy.
Want more? Go the IRC channel #TDWTFMafia on irc.slashnet.org.
|
|
-
-
Spectre


- Joined on 05-09-2007
- Posts 446
|
Re: Doing it Wrong: Unions
DemonWasp:Note: the four fields shown are all the fields in StatsResponse.
Then TRWTF is that they didn't just assign to it.
DemonWasp:Also, TRWTF: I can't find any easy way of posting properly-indented code (at
least, nothing looks correct in the Preview tab).
I find that when using the raw editor, the least troubling way to insert code is to wrap it in <pre>[code]...[/code]</pre>. You still need to C-escape \ as \\, though.
class Response {
public:
Response() {}
~Response() {}
StatsResponse& getStatsResponse() { return _statsResponse; }
void setStatsResponse ( StatsResponse& statsResponse )
{
_statsResponse.setFieldOne ( statsresponse.getFieldOne() );
_statsResponse.setFieldTwo ( statsresponse.getFieldTwo() );
_statsResponse.setFieldThree ( statsresponse.getFieldThree() );
_statsResponse.setFieldFour ( statsresponse.getFieldFour() );
}
private:
union {
StatsResponse _statsResponse;
};
};
╩юфют√ь ёЄЁрэшЎрь яюЁр эр яхэёш■.
Visit #TDWTF @ SlashNET - the semi-official WTF IRC channel.
|
|
-
-
danixdefcon5


- Joined on 01-09-2007
- Mexico City, DF, Mexico
- Posts 477
|
Re: Doing it Wrong: Unions
morbiuswilters: DogmaBites:Unions made a lot more sense in plain C, where there was no OO.
C wasn't a strongly object-oriented language, but it definitely had OO abilities.
I agree. MFC and GTK+ might be some examples of OO in C. And structs could be considered "method-less" objects as well.
|
|
-
-
DogmaBites


- Joined on 07-15-2008
- Posts 47
|
Re: Doing it Wrong: Unions
morbiuswilters: DogmaBites:Unions made a lot more sense in plain C, where there was no OO.
C wasn't a strongly object-oriented language, but it definitely had OO abilities.
OK, I was speaking loosely. Still, the only thing it has for OO is defining your own structures and treating them as different types. You couldn't pass structure A to a function that expected structure B as a parameter. Everything else had to be done by hand. If you wanted virtual functions, you created your own function table.
|
|
-
-
DogmaBites


- Joined on 07-15-2008
- Posts 47
|
Re: Doing it Wrong: Unions
danixdefcon5:I agree. MFC and GTK+ might be some examples of OO in C. And structs could be considered "method-less" objects as well.
I'm not familiar with GTK+, but MFC is a C++ library (assuming we are talking about Microsoft Foundation Classes)
|
|
-
-
FILE_NOT_FOUND


- Joined on 08-06-2008
- Posts 5
|
Re: Doing it Wrong: Unions
DogmaBites:You couldn't pass structure A to a function that expected structure B as a parameter
Of course you could, you would just cast it ;-)
|
|
-
-
belgariontheking


- Joined on 08-20-2007
- Cincinnati, OH, USA
- Posts 1,341
|
Re: Doing it Wrong: Unions
I read your entire post and did not find anything about fair wages OR vacation time.
Sir, you fail!
I guess I'm back.
Please continue to spam the addresses below.
PLEASE SPAM: jtobin@ohioinstituteofhealthcareers.edu jtobin@ohiobusinesscollege.edu
|
|
-
-
bstorer


- Joined on 02-01-2007
- Alexandria, VA
- Posts 2,208
|
Re: Doing it Wrong: Unions
DogmaBites:You couldn't pass structure A to a function that expected structure B as a parameter.
Sure you can:
struct Mommy
{
int a;
int b;
};
struct Baby
{
struct Mommy;
int c;
};
Now the compiler will accept Baby wherever Mommy is expected. Doing vtables and stuff requires a bit more magic, but it's still fairly simple.
(Note that the C standard guarantees casting back and forth between a struct and its first element only, meaning that implementing multiple inheritance this way wouldn't work.)
|
|
-
-
danixdefcon5


- Joined on 01-09-2007
- Mexico City, DF, Mexico
- Posts 477
|
Re: Doing it Wrong: Unions
DogmaBites: danixdefcon5:I agree. MFC and GTK+ might be some examples of OO in C. And structs could be considered "method-less" objects as well.
I'm not familiar with GTK+, but MFC is a C++ library (assuming we are talking about Microsoft Foundation Classes)
GTK+ runs solely in C, which gives the advantage of being able to build your GUI app in pure C. I prefer Qt, as that one was built on C++. I had little experience with MFC, but it seemed to be a lot like the GTK+ stuff, so I assumed it kind of worked like the GTK+ "OO" system.
|
|
-
-
DemonWasp


- Joined on 08-27-2008
- Posts 4
|
Re: Doing it Wrong: Unions
@bstorer: Would that solution let you pass, for example, Baby[ ] to something expecting Mommy[ ], or would it cause problems with sizeof ( Baby ) > sizeof ( Mommy ) ? But yes, that's pretty cool that you can do nonsense like that. Still, I'd prefer to write in a nicer OO environment. (Edited to put the closing brackets back in, albeit with a space between them -- Jeff)
|
|
-
-
DogmaBites


- Joined on 07-15-2008
- Posts 47
|
Re: Doing it Wrong: Unions
I'll have to check the C standard some time. I didn't know that this specific cast was implicitly done. However, I just tried it on both GCC 4.1.3 and MSVC 6.0 and both complain about incompatible types, along with "declaration does not declare anything" for the inner Mommy. I tried putting a specific member name for the inner Mommy, but that did not help with the incompatible type warning. It would definitely be a cool thing If the casting was implicit (that is the way I read your post). But my admittedly cursory check says no :(.
|
|
-
-
bstorer


- Joined on 02-01-2007
- Alexandria, VA
- Posts 2,208
|
Re: Doing it Wrong: Unions
DemonWasp:@bstorer: Would that solution let you pass, for example, Baby[ to something expecting Mommy
Yes.
DemonWasp:or would it cause problems with sizeof ( Baby ) > sizeof ( Mommy ) ?
No, because for all intents and purposes, the compiler would treat Baby as a Mommy.
|
|
-
-
morbiuswilters


- Joined on 01-15-2008
- Cambridge, MA
- Posts 2,860
|
Re: Doing it Wrong: Unions
bstorer:No, because for all intents and purposes, the compiler would treat Baby as a Mommy.
Just like the Liberal education system that hands out condoms to first-graders.
< pstorer> Bans don't mean shit on the forum. It's like being on the Sex Offender List. You can still entice kids into your van with candy.
Want more? Go the IRC channel #TDWTFMafia on irc.slashnet.org.
|
|
-
-
DemonWasp


- Joined on 08-27-2008
- Posts 4
|
Re: Doing it Wrong: Unions
Ah poop, the forum deleted the square brackets. I meant to ask whether Baby-array versus Mommy-array would make any difference, but apparently square brackets get eaten by the software. My line of thinking here is that since arrays of non-pointers are stored sequentially, a function expecting Baby (size 8, say) and getting Mommy (size 12) would index incorrectly into the array - after all, it's pointer arithmetic on the CPU. @morbius: Handing out condoms to highschoolers is a better solution than preaching abstinence-only sex-ed. "Neither" is probably a better bet, and that's what I got in high school.
|
|
-
-
Jeff S


- Joined on 11-22-2004
- Boston MA
- Posts 1,003
|
Re: Doing it Wrong: Unions
DemonWasp:@morbius: Handing out condoms to highschoolers is a better solution than preaching abstinence-only sex-ed. "Neither" is probably a better bet, and that's what I got in high school. Morbius is joking/trolling .... any further discussion on this will get deleted ... feel free to discuss offline. Thanks!
I did not become a TDWTF forum moderator to make friends. And by the way, I haven't.
|
|
-
-
joe.edwards


- Joined on 08-14-2006
- Dallas, TX
- Posts 228
|
Re: Doing it Wrong: Unions
I was very disappointed when I discovered that I could not union a BYTE with 8 bools so I could access each bit individually. Each bool takes a byte so I could really only set the highest bit of the BYTE. I ended up writing a class to do it. Of course, C/C++ don't actually fully define how the different types are stored in memory, so in some architectures there may have been more than 8 bits to a byte. That's the real WTF in my opinion.
I didn't care about portability because the platform it was running on was well-defined.
|
|
-
-
morbiuswilters


- Joined on 01-15-2008
- Cambridge, MA
- Posts 2,860
|
Re: Doing it Wrong: Unions
joe.edwards:I was very disappointed when I discovered that I could not union a BYTE with 8 bools so I could access each bit individually. Each bool takes a byte so I could really only set the highest bit of the BYTE.
lern2bitfield
< pstorer> Bans don't mean shit on the forum. It's like being on the Sex Offender List. You can still entice kids into your van with candy.
Want more? Go the IRC channel #TDWTFMafia on irc.slashnet.org.
|
|
-
-
bstorer


- Joined on 02-01-2007
- Alexandria, VA
- Posts 2,208
|
Re: Doing it Wrong: Unions
DemonWasp:I meant to ask whether Baby-array versus Mommy-array would make any difference, but apparently square brackets get eaten by the software. My line of thinking here is that since arrays of non-pointers are stored sequentially, a function expecting Baby (size 8, say) and getting Mommy (size 12) would index incorrectly into the array - after all, it's pointer arithmetic on the CPU.
You are correct; that won't work. But if you had arrays of pointers to Baby and Mommy objects you can. Consider:
#include <stdio.h>
struct Mommy
{
int a;
int b;
};
struct Baby
{
struct Mommy mommy;
int c;
};
void foo(struct Mommy * [ ]);
int main ()
{
struct Baby * baby[3];
baby[1] = malloc(sizeof(struct Baby));
baby[1]->mommy.a = 1;
baby[1]->mommy.b = 2;
baby[1]->c = 3;
foo((struct Mommy **)baby);
return 0;
}
void foo (struct Mommy * mom[ ])
{
printf("%d", mom[1]->b);
return;
}
Excuse the spaces in the square brackets. As you say, BBCode eats them otherwise.
|
|
-
-
DogmaBites


- Joined on 07-15-2008
- Posts 47
|
Re: Doing it Wrong: Unions
joe.edwards:I was very disappointed when I discovered that I could not union a BYTE with 8 bools so I could access each bit individually. Each bool takes a byte so I could really only set the highest bit of the BYTE. I ended up writing a class to do it. Of course, C/C++ don't actually fully define how the different types are stored in memory, so in some architectures there may have been more than 8 bits to a byte. That's the real WTF in my opinion.
I didn't care about portability because the platform it was running on was well-defined. In addition to morb's post, you should try to understand the purpose of language specs. By not specifying memory layout or word size, C and C++ can be compiled for a large number of platforms. An important part of writing specifications is to understand what you should not specify.
Your problem with bool and bytes is that you forget the need that each value be independently addressable and writable. If bools were truly implemented as individual bits, a bool* would have to larger than any other data pointer to include the bit position. Also, writing one bit would necessarily involve reading and writing the other 7 bits. This would make writing multithreaded code impossible. I'll simply leave the RWTF unmentioned.
|
|
-
-
ahnfelt


- Joined on 09-03-2006
- Posts 39
|
Re: Doing it Wrong: Unions
C still exists, no need for past tense. But it never had any support for OOP what so ever, at least none that machine code doesn't have. What are you thinking of?
Community Server by telligent
In-telligent: having the capacity for thought and reason
|
|
-
|
|