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:
- Improved encapsulation
- Loose coupling
- Controlled construction and destruction
Cons:
- Extra level of indirection
- Increased dynamic memory usage
References
- [PIC]: Patterns in C