C/C++

Discriminated Unions

By Dan Saks, October 18, 2012

Unions that depend on a separate field to determine access to
members can be useful in some applications  but they require careful use.

In response to some of my previous articles, several readers have
suggested that the C code I presented is preferable to the C++ code because
the C structure implementations are generally simpler than their
corresponding C++ class implementations. Other readers have claimed that my
C++ implementations are flawed because they're too simple  they don't use
inheritance and preclude the use of virtual functions.

Classes with virtual functions can be very useful, but they aren't the
solution to every problem. In this article I look at the sort
of problem that virtual functions are good at solving. I'll show a
typical C solution using a construct called a discriminated union, and
examine its limitations.

An Illustrative Problem

Suppose you have an application that employs two-dimensional geometric
shapes, such as circles, rectangles, and triangles. At a minimum, each shape
object contains some linear or angular distances sufficient to characterize
the physical extent of the shape. For example, a circle has a radius, a
rectangle has a height and a width, and a triangle has two sides and an
angle. The shapes may have common attributes as well, such a position
(planar coordinates), or outline and fill colors.

A fairly traditional C implementation for a shape is a structure with a nested union, such as:

For example, setting a shape's kind member to sk_rectangle indicates that it's now OK to access (write to or read from) the union's rectangle member and its height and width members.

A union paired with a discrete value that indicates the active member of the union is called a discriminated union or a tagged union. Some programming languages have a similar construct called a variant record. The discrete value is called a discriminator or tag. I prefer the terms discriminated union and discriminator because tag already has another meaning in C. The discriminator typically has an enumeration type, but could have an integral type.

You can write a small assortment of initialization functions to properly initialize each kind of shape. For example:

initializes a shape as a rectangle with a particular height and width. You can write similar initialization functions for circle and triangles. Using these functions, you can create an array of assorted shapes as follows:

Most of the loop body in Listing One is a switch statement
that computes the area of a shape. This is arguably a fundamental shape
operation that would be better packaged as an operation all by itself:

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task.
However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Video

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!