Menu
Coddy logo textTech

Generic Wrapper

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

A void* alone can store any data, but it has a critical flaw: you lose track of what type it holds. To build truly useful generic containers, we need to pair the data pointer with type information. This is where the Generic Wrapper pattern comes in.

The idea is simple: create a struct that bundles a void* with an enum that identifies the stored type. This way, the wrapper always knows how to interpret its contents.

typedef enum {
    TYPE_INT,
    TYPE_STRING
} DataType;

typedef struct {
    void* data;
    DataType type;
} Wrapper;

When storing data, you allocate memory for the actual value and record its type. When retrieving, you check the type before casting:

Wrapper wrap_int(int value) {
    int* p = malloc(sizeof(int));
    *p = value;
    return (Wrapper){ .data = p, .type = TYPE_INT };
}

void print_wrapper(Wrapper* w) {
    if (w->type == TYPE_INT) {
        printf("%d\n", *(int*)w->data);
    } else if (w->type == TYPE_STRING) {
        printf("%s\n", (char*)w->data);
    }
}

This pattern is the foundation for building type-safe generic containers in C. The enum acts as a runtime type tag, letting your code make safe decisions about how to handle the stored data.

challenge icon

Challenge

Easy

Let's build a type-safe generic wrapper system that can store either an integer or a string, keeping track of what type it holds so we can safely retrieve and print the data later.

You'll organize your code across three files:

  • wrapper.h: Define a DataType enum with values TYPE_INT and TYPE_STRING. Then define a Wrapper struct that bundles a void* data pointer with a DataType field. Declare the function prototypes for creating wrappers and printing their contents.
  • wrapper.c: Implement the wrapper functionality:
    • wrap_int — takes an integer, allocates memory for it, stores the value, and returns a Wrapper with the appropriate type tag
    • wrap_string — takes a string (char*), allocates memory and copies the string, and returns a Wrapper with the string type tag
    • print_wrapper — checks the type tag and prints the data appropriately (integer as-is, string as-is)
    • free_wrapper — frees the dynamically allocated data inside the wrapper
  • main.c: Bring it all together. Read a type indicator (i for integer, s for string) followed by the value. Create the appropriate wrapper, print its contents, and free the memory.

Your program will receive two inputs:

  1. A type indicator: i for integer or s for string
  2. The value to wrap

Example output when the inputs are i and 42:

42

Example output when the inputs are s and Hello:

Hello

Example output when the inputs are i and -100:

-100

Example output when the inputs are s and Generic Programming:

Generic Programming

Remember to use include guards in your header file. For string copying, allocate enough space for the string plus the null terminator using strlen and strcpy.

Cheat sheet

The Generic Wrapper pattern pairs a void* pointer with type information to create type-safe generic containers. This allows you to store any data type while maintaining knowledge of what type is stored.

Define an enum to identify stored types:

typedef enum {
    TYPE_INT,
    TYPE_STRING
} DataType;

Create a struct that bundles the data pointer with the type tag:

typedef struct {
    void* data;
    DataType type;
} Wrapper;

When storing data, allocate memory and record the type:

Wrapper wrap_int(int value) {
    int* p = malloc(sizeof(int));
    *p = value;
    return (Wrapper){ .data = p, .type = TYPE_INT };
}

When retrieving data, check the type tag before casting:

void print_wrapper(Wrapper* w) {
    if (w->type == TYPE_INT) {
        printf("%d\n", *(int*)w->data);
    } else if (w->type == TYPE_STRING) {
        printf("%s\n", (char*)w->data);
    }
}

The enum acts as a runtime type tag, enabling safe type-checking and appropriate handling of stored data.

Try it yourself

#include <stdio.h>
#include <string.h>
#include "wrapper.h"

int main() {
    char type;
    scanf("%c", &type);
    getchar(); // consume newline
    
    Wrapper w;
    
    if (type == 'i') {
        int value;
        scanf("%d", &value);
        // TODO: Create an integer wrapper using wrap_int
        
    } else if (type == 's') {
        char str[256];
        fgets(str, sizeof(str), stdin);
        // Remove trailing newline if present
        str[strcspn(str, "\n")] = '\0';
        // TODO: Create a string wrapper using wrap_string
        
    }
    
    // TODO: Print the wrapper contents using print_wrapper
    
    // TODO: Free the wrapper memory using free_wrapper
    
    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