Handling Related Types in C

Suppose you need a program that maintains a database of all the employees in a company. The company has several different types of employee: regular employees, salespersons, managers, temporary employees, etc., and your program must be able to handle all of them.

If you're writing this program in C, you could define a structure type called employee that has fields for the name, birthdate, social security number, and other characteristics. However, each type of employee requires slightly different information. For example, a regular employee's salary is based on an hourly wage and the number of hours worked, while a salesperson's salary also includes a commission on the number of sales made, and a manager's salary is a fixed amount per week.

It's difficult to find a way to represent the information about each employee. You could define a different structure type for each type of employee, but then you couldn't write a function that worked on any kind of employee; you couldn't pass a manager structure to a function expecting an employee structure. Another possibility is to include all the possible fields in the employee structure type, but that would be a waste of space, since several fields would be empty for any given employee.

One solution in C is to define a structure that contains a union. For example:

/* Example of implementing related types in C */

struct wage_pay

{

float wage;

float hrs;

};

struct sales_pay

{

float wage;

float hrs;

float commission;

float sales_made;

};

struct mgr_pay

{

float weekly_salary;

};

enum { WAGE_EMPLOYEE, SALESPERSON, MANAGER } EMPLOYEE_TYPE;

struct employee

{

char name[30];

EMPLOYEE_TYPE type;

union

{

struct wage_pay worker;

struct sales_pay seller;

struct mgr_pay mgr;

}; // Anonymous union

};

The employee structure contains a union of the various salary structures. The program uses the type field to indicate the type of employee and to keep track of which form of salary is stored in the union.

Now consider how you would compute the salary of an employee. You might write a function that looks like this:

/* Example of type-specific processing in C */

float compute_pay( struct employee *emp )

{

switch( emp->type )

{

case WAGE_EMPLOYEE:

return emp->worker.hrs * emp->worker.wage;

break;

case SALESPERSON:

return emp->seller.hrs * emp->seller.wage +

emp->seller.commissions * emp->seller.sales_made;

break;

case MANAGER:

return emp->mgr.weekly_salary;

break;

// ...

};

}

This function uses the value of the type field to determine how it accesses the contents of the union. This way the function can perform a different salary computation for each type of employee.

Salary computation is only one example of a task that is different for each type of employee. The employee-database program might use unions and switch statements for a wide variety of tasks, such as health plan management or vacation computation.

These switch statements have a couple of disadvantages:

They can be difficult to read, especially if there is common processing for two or more types. It's also difficult to isolate the code that describes a particular type; for example, the code to handle the SalesPerson class is spread throughout the program.

They are difficult to maintain. If you add a new type of employee, you have to add a new case statement that handles that type to every switch statement in the program. This makes updating the program error-prone, since it's possible to overlook a switch statement somewhere. In addition, every time you modify the code that handles one type, you must recompile the code that handles all the other types. This can be time-consuming when you're testing code for a new type of employee.

There are other ways to write this program in C, but they require much more programming effort. C doesn't provide an easy and maintainable way to express relations among multiple user-defined types. One of the goals in designing C++ was to remedy C's weakness in this area.