Menu
Coddy logo textTech

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.

challenge icon

Desafío

Fácil

Vamos 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 enum DataType con los valores TYPE_INT y TYPE_STRING. Luego, define una estructura Wrapper que agrupe un puntero de datos void* con un campo DataType. 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 (i para entero, s para cadena) seguido del valor. Crea el wrapper apropiado, imprime su contenido y libera la memoria.

Tu programa recibirá dos entradas:

  1. Un indicador de tipo: i para entero o s para cadena
  2. El valor a envolver

Ejemplo de salida cuando las entradas son i y 42:

42

Ejemplo de salida cuando las entradas son s y Hello:

Hello

Ejemplo de salida cuando las entradas son i y -100:

-100

Ejemplo de salida cuando las entradas son s y Generic Programming:

Generic Programming

Recuerda 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;
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Object Oriented Programming