This particular page contains the material on "unions". New "unions" will rarely be defined by students learning C++ but they do occur in some quite commonly used libraries and so students need to understand their role.
You aren't limited to the compiler provided char, int, double data types and their derivatives like arrays. You can define your own data types. Most of Part IV of this text is devoted to various forms of programmer defined type. Here we introduce just three relatively simple kinds. These are almost the same as the equivalents provided in C; in contrast, C has no equivalent to the more sophisticated forms of programmer defined type introduced in Part IV.
Enumerated types, treated in section 16.1, are things that you should find useful in that they can improve the readability of your programs and they allow the compiler to do a little extra type checking that can eliminate certain forms of error. Although useful, enumerated types are not that major a factor in C++ programming.
Structs are the most important of the three language elements introduced here, section 16.2. They do have wider roles, but here we are interested in their primary role which is the grouping of related data.
Unions: treat them as a "read only" feature of C++. You will sometimes see unions being employed in library code that you use, but it is unlikely that you will find any real application for unions in the programs that you will be writing.
Unions are most easily understood from real examples The following examples are based on code from Xlib. This is a C library for computers running Unix (or variations like Mach or Linux). The Xlib library provides the code needed for a program running on a computer to communicate with an X-terminal. X-terminals are commonly used when you want a multi-window style of user interface to Unix.
An X-terminal is a graphics display device that incorporates a simple microprocessor and memory. The microprocessor in the X-terminal does part of the work of organizing the display, so reducing the computational load on the main computer.
When the user does something like move the mouse, type a character, or click an action button, the microprocessor packages this information and sends it in a message to the controlling program running on the main computer.
In order to keep things relatively simple, all such messages consist of a 96 byte block of data. Naturally, different actions require different data to be sent. A mouse movement needs a report of where the mouse is now located, a keystroke action needs to be reported in terms of the symbol entered.
Xlib-based programs use XEvent unions to represent these 96 byte blocks of data. The declaration for this union is
typedef union _XEvent { intThis declaration means that an XEvent may simply contain an integer (and 92 bytes of unspecified data), or it may contain an XAnyEvent, or it may contain an XButtonEvent, or .... There are about thirty different messages that an Xterminal can send, so there are thirty different alternative interpretations specified in the union declaration.type; XAnyEvent
xany; XButtonEvent
xbutton; XMotionEvent
xmotion; XCreateWindowEvent xcreatewindow; ... ... } XEvent;
Each of these different messages has a struct declaration that specifies the data that that kind of message will contain. Two of these structs are:
typedef struct { int type; unsigned long serial; Bool send_event; Display *display; Window window; Window root; Window subwindow; Time time; int x, y; int x_root, y_root; unsigned int state; unsigned int button; Bool same_screen; } XButtonEvent; typedef struct { int type; unsigned long serial; Bool send_event; Display *display; Window window; int x, y; int width, height; int border_width; Bool override_redirect; } XCreateWindowEvent;
As illustrated in Figure 16.1, the first part of any message is a type code. The way that the rest of the message bytes are used depends on the kind of message.
If you have an XEvent variable ev, you can access its type field using the "." operator just like accessing a data field in a normal structure:
XEvent ev; ... switch(ev.type) { ... }
If you know that ev really encodes an XCreateWindowEvent and you want to work out the area of the new window, you can use code like:
area = ev.xcreatewindow.width * eve.xcreatewindow.height;The appropriate data fields are identified using a doubly qualified name. The variable name is qualified by the name of the union type that is appropriate for the kind of record known to be present (so, for an XCreateWindowEvent, you start with ev.xcreatewindow). This name is then further qualified by the name of the data member that should be accessed (ev.xcreatewindow.width).
A programmer writing the code to deal with such messages knows that a message will start with a type code. The Xlib library has a series of #defined constants, e.g. ButtonPress, DestroyNotify, MotionNotify; the value in the type field of a message will correspond to one of these constants. This allows messages from an Xterminal to be handled as follows:
XEvent eV; ... /* Code that gets calls the Unix OS and gets details of the next message from the Xterminal copied into eV */ ... switch(ev.type) { caseButtonPress: /* code doing something depending on where the button was pressed, access using xbutton variant from union */ if((ev.xbutton.x > x_low) && (ev.xbutton.x < x_high) && ... break; case MotionNotify: /* user has moved pointer, get details of when this happened and decide what to do, access using xmotion variant from union */ thetime = ev.xmotion.time; ... break; case CreateNotify: /* We have a new window, code here that looks at where and what size, access using xcreatewindow variant of union */ int hpos = ev.xcreatewindow.x; ... break; ... }Last modified March 1996. Please email questions to [email protected]