Menu
Coddy logo textTech

Polymorphic Iteration

Part of the Object Oriented Programming section of Coddy's C journey — lesson 42 of 61.

You've learned to create interfaces and implement them with different concrete functions. The true payoff comes when you can treat a collection of different objects uniformly — iterating through them and calling the same function pointer on each, regardless of what specific behavior is wired in.

Imagine you have an array of Action structs, each with its own execute function assigned:

typedef void (*ExecuteFunc)(void);

typedef struct {
    const char* name;
    ExecuteFunc execute;
} Action;

void jump(void) { printf("Jumping!\n"); }
void run(void)  { printf("Running!\n"); }
void rest(void) { printf("Resting...\n"); }

You can create an array of these actions, each wired to a different function, then loop through them with a simple for loop:

int main() {
    Action actions[3] = {
        { "Jump", jump },
        { "Run", run },
        { "Rest", rest }
    };
    
    for (int i = 0; i < 3; i++) {
        printf("%s: ", actions[i].name);
        actions[i].execute();
    }
    return 0;
}

The loop doesn't know or care what each action does internally. It simply calls execute() on each element.

This is polymorphism in action — the same code handles objects with completely different behaviors. Adding a new action type requires no changes to the iteration logic; you just add another element to the array.

challenge icon

Challenge

Easy

Let's build a Task management system that demonstrates polymorphic iteration — processing a collection of objects that share a common interface but execute different behaviors.

You'll organize your code across three files:

  • task.h: Define your task interface here. Create a function pointer type called TaskFunc that takes no parameters and returns nothing. Then define a Task struct containing a name (a const char*) and a run function pointer of type TaskFunc.
  • task.c: Implement three different task functions that represent different types of work:
    • backup_task — prints Backing up data...
    • cleanup_task — prints Cleaning up files...
    • report_task — prints Generating report...
  • main.c: Bring everything together here. Create an array of Task structs, each wired to a different task function. Then iterate through the array and execute each task polymorphically — for each task, print its name followed by calling its run function.

Your program will receive a single input: the number of tasks to run (1, 2, or 3).

Create an array containing exactly three tasks in this order: a backup task named Backup, a cleanup task named Cleanup, and a report task named Report. Based on the input count, iterate through only that many tasks from the beginning of the array.

For each task in your iteration, print the task's name followed by a colon and space, then call its run function.

Example output when the input is 2:

Backup: Backing up data...
Cleanup: Cleaning up files...

Example output when the input is 3:

Backup: Backing up data...
Cleanup: Cleaning up files...
Report: Generating report...

The key insight is that your loop doesn't need to know what each task does — it simply prints the name and calls run() on each element. The same iteration code handles all task types uniformly. Remember to use include guards in your header file.

Cheat sheet

You can iterate through collections of objects that share a common interface, calling the same function pointer on each element regardless of its specific implementation. This is polymorphism — treating different objects uniformly.

Define a function pointer type and a struct that uses it:

typedef void (*ExecuteFunc)(void);

typedef struct {
    const char* name;
    ExecuteFunc execute;
} Action;

Create concrete functions that match the signature:

void jump(void) { printf("Jumping!\n"); }
void run(void)  { printf("Running!\n"); }
void rest(void) { printf("Resting...\n"); }

Build an array of structs, each wired to a different function, then iterate through them:

Action actions[3] = {
    { "Jump", jump },
    { "Run", run },
    { "Rest", rest }
};

for (int i = 0; i < 3; i++) {
    printf("%s: ", actions[i].name);
    actions[i].execute();
}

The loop doesn't need to know what each action does internally — it simply calls the function pointer on each element. Adding new types requires no changes to the iteration logic.

Try it yourself

#include <stdio.h>
#include "task.h"

// Declare the task functions implemented in task.c
void backup_task(void);
void cleanup_task(void);
void report_task(void);

int main() {
    int count;
    scanf("%d", &count);
    
    // TODO: Create an array of 3 Task structs in this order:
    // 1. Backup task named "Backup" using backup_task
    // 2. Cleanup task named "Cleanup" using cleanup_task
    // 3. Report task named "Report" using report_task
    
    // TODO: Iterate through 'count' tasks from the array
    // For each task, print: "<name>: " then call its run function
    
    return 0;
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming