Sean Bollin Sean Bollin - 1 year ago 63
C++ Question

Why is this union apparently holding more than one value?

I'm new to using unions and confused as to how this test is passing:

TEST(basic_check, test_eq) {
Dot dot;

SDL_Event event;
event.type = SDL_KEYDOWN;

SDL_Keysym keysym;
keysym.sym = SDLK_UP;
event.key.keysym = keysym;


EXPECT_EQ(-Dot::DOT_VEL, dot.getVelY());

My understanding is that a union can ever only hold one value. In case you don't know, SDL_Event is a union and event.key and SDL_Keysym are structs.

void Dot::handleEvent(SDL_Event& e) {

if (e.type == SDL_KEYDOWN && e.key.repeat == 0) {
switch (e.key.keysym.sym) {
case SDLK_UP:
velY -= DOT_VEL;
velY += DOT_VEL;
velX -= DOT_VEL;
velX += DOT_VEL;

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 */
SDL_TextEditingEvent edit; /**< Text editing event data */
SDL_TextInputEvent text; /**< Text input event data */
SDL_MouseMotionEvent motion; /**< Mouse motion event data */
SDL_MouseButtonEvent button; /**< Mouse button event data */
SDL_MouseWheelEvent wheel; /**< Mouse wheel event data */
SDL_JoyAxisEvent jaxis; /**< Joystick axis event data */
SDL_JoyBallEvent jball; /**< Joystick ball event data */
SDL_JoyHatEvent jhat; /**< Joystick hat event data */
SDL_JoyButtonEvent jbutton; /**< Joystick button event data */
SDL_JoyDeviceEvent jdevice; /**< Joystick device change event data */
SDL_ControllerAxisEvent caxis; /**< Game Controller axis event data */
SDL_ControllerButtonEvent cbutton; /**< Game Controller button event data */
SDL_ControllerDeviceEvent cdevice; /**< Game Controller device event data */
SDL_AudioDeviceEvent adevice; /**< Audio device event data */
SDL_QuitEvent quit; /**< Quit request event data */
SDL_UserEvent user; /**< Custom event data */
SDL_SysWMEvent syswm; /**< System dependent window event data */
SDL_TouchFingerEvent tfinger; /**< Touch finger event data */
SDL_MultiGestureEvent mgesture; /**< Gesture event data */
SDL_DollarGestureEvent dgesture; /**< Gesture event data */
SDL_DropEvent drop; /**< Drag and drop event data */

/* This is necessary for ABI compatibility between Visual C++ and GCC
Visual C++ will respect the push pack pragma and use 52 bytes for
this structure, and GCC will use the alignment of the largest datatype
within the union, which is 8 bytes.

So... we'll add padding to force the size to be 56 bytes for both.
Uint8 padding[56];
} SDL_Event;

SDL_KeyboardEvent is defined like this:

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;

Answer Source

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) .