Reputation: 910
I'm new to using unions and confused as to how this test is passing, SDL_Event
being a union:
TEST(basic_check, test_eq) {
Dot dot;
SDL_Event event; // this is a union, see below
event.type = SDL_KEYDOWN; // <= I use one member here
SDL_Keysym keysym; // this is a struct
keysym.sym = SDLK_UP;
event.key.keysym = keysym; // <= I use another member here
dot.handleEvent(event); // <= but this function accesses value of the first member
EXPECT_EQ(-Dot::DOT_VEL, dot.getVelY());
}
My understanding is that a union can ever only hold one value.
In this test however, I set a value into event.type
, one member of the union; Then I update event.key
, another member of the union. More precisely event.key
is a struct and I update its member having a struct SDL_Keysym
.
Here the code of the function that is then called:
void Dot::handleEvent(SDL_Event& e) {
if (e.type == SDL_KEYDOWN && e.key.repeat == 0) { //<== access two alternate members?
switch (e.key.keysym.sym) {
case SDLK_UP:
velY -= DOT_VEL;
break;
case SDLK_DOWN:
... // followed by a lot of other cases
}
}
}
I'm confused, because the if
condition accesses two members of the union (see comment above). I thought they would be exclusive.
For information, SDL_Event
and SDL_KeyboardEvent
are defined like this:
typedef union SDL_Event
{
Uint32 type; /**< Event type, shared with all events */
SDL_CommonEvent common; /**< Common event data */
SDL_WindowEvent window; /**< Window event data */
SDL_KeyboardEvent key; /**< Keyboard event data */
... // and a long list of other events
...
} SDL_Event;
typedef struct SDL_KeyboardEvent
{
Uint32 type; /**< ::SDL_KEYDOWN or ::SDL_KEYUP */
Uint32 timestamp;
Uint32 windowID; /**< The window with keyboard focus, if any */
Uint8 state; /**< ::SDL_PRESSED or ::SDL_RELEASED */
Uint8 repeat; /**< Non-zero if this is a key repeat */
Uint8 padding2;
Uint8 padding3;
SDL_Keysym keysym; /**< The key that was pressed or released */
} SDL_KeyboardEvent;
Upvotes: 1
Views: 312
Reputation: 73376
You are right about unions having at most one member active at any given time.
But the standard makes a guarantee, in order to facilitate the use of the union (and in particular to find out, like here, which is the active element):
9.5/1: (...) If a standard-layout union contains several standard-layout structs that share a common initial sequence, and if an object of this standard-layout union type contains one of the standard-layout structs, it is permitted to inspect the common initial sequence of any of standard-layout struct members;
In your example, the SDL_Event
union has a union member Uint32 type
and all the SDL_XXXEvent
structures start as well with an Uint32
. This is the common initial sequence, so that it can be inspected using any of the members (and the simplest one is just type
).
Edit: interesting remark (taken over from the comments)
As you've pointed out, the test is not only inspecting: it also writes in type
using the event.type
and then assign the keysym
in event.key
. You therefore wonder if the change of active member (from type
to key
) wouldn't invalidate common initial sequence.
Be assured that this works perfectly. The C++ guarantee on inspection ensures that after the assignment of event.type
(common initial sequence), event.key.type
is also SDL_KEYDOWN
. As you then change only event.key.keysim
there is no reason that type
's value changes.
Note however that timestamp
, WindowsID
and the other members of event.key
are in an undefined state. Your test doesn't use them, so there is no reason to fail. But to avoid this kind of potential issues, a better approach could be to construct an SDL_KeyboardEvent
, initialize it properly and copy the whole struct it into event.key
Upvotes: 4
Reputation: 409176
If you look closer at SDL_Event
you will see that it's mostly a unio of structures, where each structure have the same initial signature (having a single 8-bit unsigned integer as the first member).
That is what makes it work. Even if the structures in the union will have different size, all will have the same member type
as the first member.
That is a simple way to emulate inheritance, meaning that all the structures in the SDL_Event
union to be siblings to each other.
Also, using a union to represent multiple types as a kind of type punning is explicitly allowed by the C specification.
However... Since the question is tagged as a C++ question, this is technically undefined behavior.
For backwards compatibility with C most (if not all) C++ compilers will allow this without any complaints.
Upvotes: 0