Refactored Solution: From Static to Dynamic Dispatch
This directory contains the refactored version of Ch02, demonstrating how function pointers eliminate switch statements and improve code organization.
What Changed from Ch02?
1. Removed the Type Enum
Ch02 (Static Dispatch):
enum payload_kind {
COMMAND_LOGIN, COMMAND_JOIN, COMMAND_LOGOUT,
MESSAGE_DIRECT, MESSAGE_GROUP, MESSAGE_GLOBAL
};
struct payload {
enum payload_kind kind; // Type tag
union payload_data data;
};
Ch03 (Dynamic Dispatch):
struct payload {
void (*process)(const struct payload *self); // Behavior
void (*destroy)(const struct payload *self); // Cleanup
union payload_data data;
};
Impact: No more type enumeration needed. Each payload carries its own behavior through function pointers.
2. Eliminated Switch Statements for Destroy and Process
Ch02:
void process_next(struct payload_buffer *buf) {
struct payload *p = &buf->payloads[buf->process_base];
switch (p->kind) {
case COMMAND_LOGIN:
printf("Command: login\n");
printf(" Arguments: [username: %s, password: %s]\n",
p->data.command_login.username,
p->data.command_login.password);
break;
case COMMAND_JOIN:
// ... more cases
// ... 6 total cases
}
buf->process_base++;
}
Ch03:
void process_next(struct payload_buffer *buf) {
struct payload *p = &buf->payloads[buf->process_base];
p->process(p); // Dynamic dispatch!
buf->process_base++;
}
Impact: From 30 lines with 6 cases to 3 lines. Each payload knows how to process itself.
3. File Organization for Isolation
Ch02 Structure:
traditional_dispatch.h: Everything (types, buffer, functions)traditional_dispatch.c: All logic mixed together
Ch03 Structure:
payload.h/payload.c: Payload types and behaviors (process/destroy functions)dynamic_dispatch.h/dynamic_dispatch.c: Buffer management and orchestration
Impact: Better isolation between concerns. Payload behavior is separated from buffer orchestration. We will elaborate isolation on later chapters, but here is a quick explaination:
What is Isolation?
Isolation means separating different concerns so changes in one area do not ripple through unrelated areas.
In Ch02:
- Adding a new payload type required changes in multiple functions
- Buffer logic and payload logic were intertwined
- Everything depended on the shared enum
In Ch03:
- Payload behaviors are self-contained (each has its own process/destroy functions)
- Buffer management is isolated in
dynamic_dispatch.c - Adding a new payload type only requires:
- Create new process/destroy functions in
payload.c - Modify
push_payload()to assign those functions - Done! No changes to
process_next()ordestroy()
- Create new process/destroy functions in
Benefits of isolation:
- Easier to understand: Each file has a single, clear purpose
- Safer to modify: Changes don’t accidentally break unrelated code
- Better for teams: Different developers can work on payloads vs. buffer logic without conflicts
Side Quest: Further Isolation
Currently, push_payload() does two things:
- Parses the raw string and constructs a payload
- Adds it to the buffer
Challenge: Extract parsing logic into a separate function:
struct payload parse_payload(const char *raw);
This function should:
- Parse the input string
- Assign appropriate function pointers
- Return a constructed payload
Then push_payload() becomes simpler:
void push_payload(struct payload_buffer *buf, const char *raw) {
struct payload parsed;
bool is_parsing_successful = parse_payload(&parsed, raw); // Construct
// ... add to buffer (if is_parsing_successful)
}
Why? Parsing logic is now isolated from buffer management. You can test payload construction independently, and changing the buffer structure does not affect parsing.
Remaining Limitations
Even with function pointers, we still have problems:
- Construction is still static:
push_payload()still uses if/else to assign function pointers - Repetitive function pointers: Each payload struct stores two pointers (process + destroy)
- No type safety: Nothing prevents mixing incompatible function pointers
Next: The extended version implements the new requirements (multiple receivers) to further demonstrate the benefits of dynamic dispatch.