Wrapper genérico
Parte de la sección Object Oriented Programming del Journey de C de Coddy — lección 50 de 61.
Un void* por sí solo puede almacenar cualquier dato, pero tiene un fallo crítico: se pierde el rastro de qué tipo contiene. Para construir contenedores genéricos verdaderamente útiles, necesitamos emparejar el puntero de datos con información de tipo. Aquí es donde entra en juego el patrón Generic Wrapper.
La idea es simple: crear un struct que agrupe un void* con un enum que identifique el tipo almacenado. De esta manera, el wrapper siempre sabe cómo interpretar su contenido.
typedef enum {
TYPE_INT,
TYPE_STRING
} DataType;
typedef struct {
void* data;
DataType type;
} Wrapper;Al almacenar datos, se asigna memoria para el valor real y se registra su tipo. Al recuperarlos, se verifica el tipo antes de realizar la conversión (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);
}
}Este patrón es la base para construir contenedores genéricos con seguridad de tipos en C. El enum actúa como una etiqueta de tipo en tiempo de ejecución, permitiendo que su código tome decisiones seguras sobre cómo manejar los datos almacenados.
Desafío
FácilVamos a construir un sistema de envoltura (wrapper) genérico y seguro para tipos que pueda almacenar un entero o una cadena, manteniendo el rastro de qué tipo contiene para que podamos recuperar e imprimir los datos de forma segura más tarde.
Organizarás tu código en tres archivos:
wrapper.h: Define un enumDataTypecon los valoresTYPE_INTyTYPE_STRING. Luego, define una estructuraWrapperque agrupe un puntero de datosvoid*con un campoDataType. Declara los prototipos de las funciones para crear wrappers e imprimir su contenido.wrapper.c: Implementa la funcionalidad del wrapper:wrap_int— toma un entero, asigna memoria para él, almacena el valor y devuelve un Wrapper con la etiqueta de tipo adecuada.wrap_string— toma una cadena (char*), asigna memoria y copia la cadena, y devuelve un Wrapper con la etiqueta de tipo de cadena.print_wrapper— verifica la etiqueta de tipo e imprime los datos adecuadamente (entero tal cual, cadena tal cual).free_wrapper— libera la memoria asignada dinámicamente dentro del wrapper.
main.c: Une todo. Lee un indicador de tipo (ipara entero,spara cadena) seguido del valor. Crea el wrapper apropiado, imprime su contenido y libera la memoria.
Tu programa recibirá dos entradas:
- Un indicador de tipo:
ipara entero ospara cadena - El valor a envolver
Ejemplo de salida cuando las entradas son i y 42:
42Ejemplo de salida cuando las entradas son s y Hello:
HelloEjemplo de salida cuando las entradas son i y -100:
-100Ejemplo de salida cuando las entradas son s y Generic Programming:
Generic ProgrammingRecuerda usar guardas de inclusión (include guards) en tu archivo de cabecera. Para copiar cadenas, asigna suficiente espacio para la cadena más el terminador nulo usando strlen y strcpy.
Hoja de referencia
El patrón Generic Wrapper (Envoltorio Genérico) empareja un puntero void* con información de tipo para crear contenedores genéricos seguros en cuanto a tipos. Esto le permite almacenar cualquier tipo de datos manteniendo el conocimiento de qué tipo está almacenado.
Defina un enum para identificar los tipos almacenados:
typedef enum {
TYPE_INT,
TYPE_STRING
} DataType;Cree una estructura que agrupe el puntero de datos con la etiqueta de tipo:
typedef struct {
void* data;
DataType type;
} Wrapper;Al almacenar datos, asigne memoria y registre el tipo:
Wrapper wrap_int(int value) {
int* p = malloc(sizeof(int));
*p = value;
return (Wrapper){ .data = p, .type = TYPE_INT };
}Al recuperar datos, verifique la etiqueta de tipo antes de realizar la conversión (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);
}
}El enum actúa como una etiqueta de tipo en tiempo de ejecución, lo que permite una comprobación de tipos segura y un manejo adecuado de los datos almacenados.
Pruébalo tú mismo
#include <stdio.h>
#include <string.h>
#include "wrapper.h"
int main() {
char type;
scanf("%c", &type);
getchar(); // consume el salto de línea
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);
// Elimina el salto de línea final si está presente
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;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Object Oriented Programming
1Fundamentos de programación modular
Archivos de cabeceraGuardas de inclusiónArchivos fuenteFunciones estáticasRepaso: Calculadora modular4Encapsulamiento
Concepto de punteros opacosDefinición de structs opacosGetters y SettersValidación en settersResumen: Caja secreta7Punteros a funciones
Declaración de punteros a funcionesLlamada a punteros a funcionesTypedef para punteros a funcionesPasar funciones como argumentosRepaso: Dispatch de calculadora10Contenedores genéricos
Repaso de punteros voidWrapper genéricoSwap genéricoComparación genéricaRepaso: Array genérico2Objetos y métodos
Structs como objetosEl puntero 'Self'Const CorrectnessPuntero vs. ValorMétodos auxiliaresResumen: Point Manager5Proyecto: Cuenta bancaria simple
Configuración del proyectoImplementación de la cuenta3Ciclo de vida de los objetos
Patrón ConstructorPatrón DestructorInicialización en StackCopia profundaResumen: String Wrapper6Herencia mediante composición
Incrustación de StructsLa regla del primer miembroAcceso a miembros padreUpcastingResumen: Jerarquía de formas9Proyecto: Dibujador de figuras
Descripción general del proyectoImplementación del círculo