Союзы, содержащие элемент atype

У меня вопрос о том, чего я до сих пор не понимаю в профсоюзах. Я читал об их использовании и по большей части вижу, чем они могут быть полезны, и понимаю их. Я видел, что они могут обеспечить примитивный полиморфизм в стиле C. Примером этого, который я видел на нескольких веб-сайтах, является объединение событий SDL:

typedef union {
 Uint8 type;
 SDL_ActiveEvent active;
 SDL_KeyboardEvent key;
 SDL_MouseMotionEvent motion;
 SDL_MouseButtonEvent button;
 SDL_JoyAxisEvent jaxis;
 SDL_JoyBallEvent jball;
 SDL_JoyHatEvent jhat;
 SDL_JoyButtonEvent jbutton;
     SDL_ResizeEvent resize;
 SDL_ExposeEvent expose;
 SDL_QuitEvent quit;
 SDL_UserEvent user;
     SDL_SysWMEvent syswm;
} SDL_Event;

Я не могу понять, как там может сосуществовать член "типа" с типами событий? Разве им не разрешено существовать только по одному, поскольку они занимают одну и ту же область памяти? Разве союз не существовал бы когда-либо как тип или как одно из событий?

Я понимаю, что каждое событие на самом деле представляет собой структуру с членом типа, например:

// SDL_MouseButtonEvent

typedef struct{
     Uint8 type;
     Uint8 button;
     Uint8 state;
     Uint16 x, y;
} SDL_MouseButtonEvent;

Какой в ​​этом смысл? Позволяет ли это каким-то образом члену типа объединения представлять тип любой структуры, которая в настоящее время является объединением? Это какой-то странный эффект, который происходит, когда каждый член союза, кроме одного, является структурой, и каждая структура содержит этот один член?

Можете ли вы получить доступ к членам структуры, не зная, какой структурой является объект?

Спасибо!


person Russel    schedule 09.02.2011    source источник
comment
Убедитесь, что поле Uint8 type связано с другими полями. Я считаю, что поле type не имеет ничего общего с другими полями. Это просто еще одно поле.   -  person Thomas Matthews    schedule 09.02.2011


Ответы (6)


Если каждый из типов событий имеет Uint8 в качестве своего первого члена данных, то член type union просто удобен.

Общее правило для объединений заключается в том, что вы можете получить доступ к данным в объединении только с помощью последнего члена данных, в который вы записали. Итак, если вы в последний раз писали active, вы не сможете в следующий раз прочитать key.

Исключением из этого правила является то, что если несколько членов объединения используют один и тот же префикс (если их первые элементы данных совпадают), вы можете получить доступ к этому префиксу через любой из элементов данных объединения, которые имеют общий префикс. Итак, здесь вы можете ссылаться на active.type или key.type, независимо от того, какой член данных объединения активен, и это сработает.

Член type SDL_Event - это просто удобный ярлык, который позволяет вам получить доступ к этому полю type, не определяя его как event_object.active.type или event_object.key.type. Вы можете просто использовать event_object.type.

person James McNellis    schedule 09.02.2011
comment
+1, изящная, вторая полезная вещь, которую я узнал сегодня ... Думаю, это очевидно, если подумать, но иногда это очевидные вещи, которых я скучаю ... :) - person Nim; 09.02.2011
comment
Я до сих пор считаю эту конструкцию необычной, я видел раньше такие вещи, как: struct SDLEvent { Uint8 type; union { type1 t1; type2 t2... } }; - person David Rodríguez - dribeas; 09.02.2011
comment
@ Дэвид: Я тоже думаю, что это немного необычно; Я собирался предложить этот подход, но тогда мне стало интересно, есть ли какие-то преимущества в подходе, принятом SDL. Я не могу придумать никакой выгоды, но я действительно не эксперт по профсоюзам; Я никогда особо ими не пользовался. Может быть, это просто необычный выбор дизайна ... - person James McNellis; 09.02.2011
comment
Во-первых, способ, которым SDL делает это, позволяет выполнять прямое преобразование в стиле C в обе стороны между SDLEvent* и SDLActiveEvent* и т. Д. (Другой способ тоже, но с дополнительным набором текста). - person aschepler; 09.02.2011
comment
@ Джеймс: Интересно! Но сколько членов квалифицируется как несколько (в отношении нескольких членов союза используется один и тот же префикс). - person Jacob; 09.02.2011
comment
@Jacob: Столько же участников, сколько есть. Это вся общая начальная последовательность разделяемых членов данных. - person James McNellis; 09.02.2011
comment
@James Является ли это необычным, зависит от того, сколько таких случаев вы видели, или, если на то пошло, написано; из моей 30-летней карьеры написания C, я бы назвал это обычным. Преимущество состоит в том, что вы можете выполнить malloc для SDL_ * и установить его поле типа без необходимости выполнять странную арифметику, чтобы получить размер malloc или вернуться к типу объединения. Если вы думаете о полиморфных типах и наследовании, это естественная структура - подтип содержит все члены своего супертипа. - person Jim Balter; 09.02.2011
comment
@ Джим: Ну, я с готовностью признал, что не особо разбираюсь в идиоматическом использовании союзов :-). Вы сделали отличное замечание. - person James McNellis; 09.02.2011

Давайте посмотрим, как объединение раскладывается в памяти:

Address:    Uint8    MouseButtonEvent   KeyboardEvent
x+0x0       type     type               type
x+0x1       -        button             ?
x+0x2       -        state              ?
...

Так уж получилось, что все члены type выстраиваются в линию, поэтому независимо от того, какой это тип, доступ к объединению как Uint8 даст фактический тип события.

person Anon.    schedule 09.02.2011

Если первая пара байтов каждой из этих структур SDL_xyz является их собственным полем типа, это означает, что, когда объединение содержит один из этих объектов, первая пара байтов объединения - это та же первая пара байтов, что и структура SDL, то есть поле типа .

Объединение не содержит и того, и другого, оно просто содержит объект SDL, первый член которого совпадает по типу, размеру и расположению с полем «тип».

person leegent    schedule 09.02.2011

Стандарт требует, чтобы союзы в C / C ++ были выровнены по самому строгому типу, который они содержат. Кроме того, поскольку элементы структур не могут быть переупорядочены и из-за требований текущих стандартов (изменяющихся в C ++ 0x), что объединения содержат только типы POD, идея состоит в том, что член типа объединения отображается на член типа в структура колоды, которые она содержит.

person Washu    schedule 09.02.2011

Что касается объединения, то реальной разницы между структурой и примитивным типом нет. Структура SDL_MouseButtonEven - это просто набор типов, один за другим.
Член объединения type занимает место члена type структур событий. графически это выглядит так:

SDL_Event union:
  type:    |--------|
  motion:  |--type--|-button-|--state-|-------x--------|-------y--------|  
  active:  |--type--|----------something-else-of-another-event--|          
  key:     |--type--|--maybe-a-smaller-event-|                             
  ... etc'
person shoosh    schedule 09.02.2011

[Отредактировано благодаря комментарию Джеймса.]

Стандарт дает некоторые особые гарантии для профсоюзов, чтобы подобные вещи были безопасными.

Важное правило состоит в том, что если две структуры POD в объединении начинаются с одних и тех же типов членов, вам разрешается использовать «общую начальную последовательность» общих членов. Таким образом, если установлено key.type, можно читать button.type, и оно будет иметь то же значение.

Кроме того, простой член данных (фундаментального типа) type непосредственно в объединении должен быть размещен в памяти, как если бы он находился в struct, содержащем только этот член. Другими словами, type также эквивалентно key.type, button.type и т. Д.

person aschepler    schedule 09.02.2011
comment
Насколько я понимаю, это полностью гарантировано для объединений POD, потому что каждый элемент данных выделяется, как если бы он был единственным членом структуры (C ++ 03 9.5 / 1). Все элементы данных должны иметь один и тот же адрес, потому что не может быть безымянного заполнения перед первым членом структуры. - person James McNellis; 09.02.2011
comment
@ Джеймс Ага, спасибо! Я не знаю, откуда я пришел к такому выводу, если только не прочитал 9.2 и не пропустил это предложение в 9.5. - person aschepler; 09.02.2011