Patterns in C

First-class ADT

Bad:

/* Include guards and include files omitted. */

#define MAX_NO_OF_ORDERS 42

/* Internal representation of a customer. */

typedef struct

{
    const char* name;
    Address address;
    size_t noOfOrders;
    Order orders[MAX_NO_OF_ORDERS];
} Customer;

void initCustomer(Customer* theCustomer,
                  const char* name,
                  const Address* address);

void placeOrder(Customer *customer, const Order* order);

/* A lot of other related functions... */

Good:

Information hiding

The First-class ADT pattern will eliminate dependency problems. Thuis pattern provides a method that separates interface from implementation.

Incomplete Types

The C standard (C99) allows us to declare objects of incomplete types in a context where their sizes aren’t needed.

In the following code:

/* Pointer to an incomplete type */
typedef struct Customer* CustomerPtr;

Instances of this pointer will serve as a handle for the clients of a first-class ADT. This mechanism enforces the constraint on clients to use the provided interface functions (Customer.h) because there is no way a client can access a field in the Customer structure (the C language does not allow an incomplete type to be de-referenced). The type is considered complete as soon as the compiler detects a subsequent specifier (Customer.c), with the same tag, and a declaration list containing the members.

Copy Semantics

Clients only use a handle, which is declared as a pointer, to the ADT. Copies of a handle are simply pointer assignment.

Dependencies managed

Internals of the data structure are encapsulated in the implementation and clients cannot access them.

Consequences

Pros:

Cons:


References