我很擅长使用工会,并对此测试的传递方式感到困惑,SDL_Event
是一个联盟:
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());
}
我的理解是,工会只能拥有一个价值。
然而,在这个测试中,我将一个值设置为event.type
,一个联盟成员;然后我更新了工会的另一名成员event.key
。更准确地说,event.key
是一个结构,我更新了它的结构SDL_Keysym
。
这里是函数的代码:
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
}
}
}
我很困惑,因为if
条件访问了两个联盟成员(请参阅上面的评论)。我以为他们会独占。
有关信息,SDL_Event
和SDL_KeyboardEvent
的定义如下:
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;
答案 0 :(得分:4)
对于在任何给定时间至多有一名成员活跃的工会,你是对的。
但标准作出了保证,以便于联合的使用(特别是找出,就像这里,哪个是活动元素):
9.5 / 1:(...)如果标准布局联合包含多个标准布局结构 共享一个共同的初始序列,如果是一个对象 标准布局联合类型包含标准布局之一 结构,允许检查任何共同的初始序列 标准布局结构成员;
在您的示例中,SDL_Event
联盟有一个联盟成员Uint32 type
,所有SDL_XXXEvent
结构也都以Uint32
开头。这是常见的初始序列,因此可以使用任何成员进行检查(最简单的只是type
)。
编辑:有趣的评论(从评论中接过)
正如您所指出的,测试不仅仅是检查:它还使用type
在event.type
中写入,然后在keysym
中分配event.key
。因此,您想知道活动成员(从type
到key
)的更改是否会使常见的初始序列无效。
请放心,这完美无缺。检查时的C ++保证确保在分配event.type
(公共初始序列)后,event.key.type
也是SDL_KEYDOWN
。然后,当您仅更改event.key.keysim
时,没有理由type
的值发生变化。
但请注意,timestamp
,WindowsID
和event.key
的其他成员处于未定义状态。您的测试不使用它们,因此没有理由失败。但是为了避免这种潜在问题,更好的方法可能是构建SDL_KeyboardEvent
,正确初始化并将整个结构复制到event.key
答案 1 :(得分:0)
如果你仔细观察SDL_Event
,你会发现它主要是一个结构,其中每个结构都有相同的初始签名(只有一个8位)无符号整数作为第一个成员)。
那是让它发挥作用的原因。即使联合中的结构具有不同的大小,所有成员都将具有与第一个成员相同的成员type
。
这是一种模拟继承的简单方法,这意味着SDL_Event
联合中的所有结构都是彼此的兄弟。
此外,C规范明确允许使用union来表示多种类型type punning。
但是...... 由于问题被标记为C ++问题,因此技术上是未定义的行为。
为了向后兼容C,大多数(如果不是全部)C ++编译器都允许这样做,没有任何抱怨。