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
EasyLet'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 aDataTypeenum with valuesTYPE_INTandTYPE_STRING. Then define aWrapperstruct that bundles avoid*data pointer with aDataTypefield. 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 tagwrap_string— takes a string (char*), allocates memory and copies the string, and returns a Wrapper with the string type tagprint_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 (ifor integer,sfor string) followed by the value. Create the appropriate wrapper, print its contents, and free the memory.
Your program will receive two inputs:
- A type indicator:
ifor integer orsfor string - The value to wrap
Example output when the inputs are i and 42:
42Example output when the inputs are s and Hello:
HelloExample output when the inputs are i and -100:
-100Example output when the inputs are s and Generic Programming:
Generic ProgrammingRemember 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;
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Modular Programming Basics
Header FilesInclude GuardsSource FilesStatic FunctionsRecap: Modular Calculator4Encapsulation
Opaque Pointers ConceptDefining Opaque StructsGetters and SettersValidation in SettersRecap: Secret Box7Function Pointers
Declaring Function PointersCalling Function PointersTypedef for Function PointersPassing Functions as ArgumentsRecap: Calculator Dispatch10Generic Containers
Void Pointers RecapGeneric WrapperGeneric SwapGeneric CompareRecap: Generic Array2Objects and Methods
Structs as ObjectsThe 'Self' PointerConst CorrectnessPointer vs ValueHelper MethodsRecap: Point Manager5Project: Simple Bank Account
Project SetupImplementation of Account3Object Lifecycle
Constructor PatternDestructor PatternStack InitializationDeep CopyRecap: String Wrapper6Inheritance via Composition
Struct EmbeddingThe First Member RuleAccessing Parent MembersUpcastingRecap: Shape Hierarchy