为什么这个联盟显然拥有多个价值?

时间:2016-09-04 00:27:16

标签: c++ sdl unions

我很擅长使用工会,并对此测试的传递方式感到困惑,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_EventSDL_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;

2 个答案:

答案 0 :(得分:4)

对于在任何给定时间至多有一名成员活跃的工会,你是对的。

但标准作出了保证,以便于联合的使用(特别是找出,就像这里,哪个是活动元素):

  

9.5 / 1:(...)如果标准布局联合包含多个标准布局结构   共享一个共同的初始序列,如果是一个对象   标准布局联合类型包含标准布局之一   结构,允许检查任何共同的初始序列   标准布局结构成员;

在您的示例中,SDL_Event联盟有一个联盟成员Uint32 type,所有SDL_XXXEvent结构也都以Uint32开头。这是常见的初始序列,因此可以使用任何成员进行检查(最简单的只是type)。

编辑:有趣的评论(从评论中接过)

正如您所指出的,测试不仅仅是检查:它还使用typeevent.type中写入,然后在keysym中分配event.key。因此,您想知道活动成员(从typekey)的更改是否会使常见的初始序列无效。

请放心,这完美无缺。检查时的C ++保证确保在分配event.type(公共初始序列)后,event.key.type也是SDL_KEYDOWN。然后,当您仅更改event.key.keysim时,没有理由type的值发生变化。

但请注意,timestampWindowsIDevent.key的其他成员处于未定义状态。您的测试不使用它们,因此没有理由失败。但是为了避免这种潜在问题,更好的方法可能是构建SDL_KeyboardEvent,正确初始化并将整个结构复制到event.key

答案 1 :(得分:0)

如果你仔细观察SDL_Event,你会发现它主要是一个结构,其中每个结构都有相同的初始签名(只有一个8位)无符号整数作为第一个成员)。

是让它发挥作用的原因。即使联合中的结构具有不同的大小,所有成员都将具有与第一个成员相同的成员type

这是一种模拟继承的简单方法,这意味着SDL_Event联合中的所有结构都是彼此的兄弟。

此外,C规范明确允许使用union来表示多种类型type punning

但是...... 由于问题被标记为C ++问题,因此技术上是未定义的行为

为了向后兼容C,大多数(如果不是全部)C ++编译器都允许这样做,没有任何抱怨。

相关问题