Wrapper Genérico
Parte da seção Object Oriented Programming do Journey de C da Coddy — lição 50 de 61.
Um void* sozinho pode armazenar qualquer dado, mas ele tem uma falha crítica: você perde o rastro de qual tipo ele contém. Para construir contêineres genéricos verdadeiramente úteis, precisamos emparelhar o ponteiro de dados com informações de tipo. É aqui que entra o padrão Wrapper Genérico.
A ideia é simples: crie uma struct que agrupe um void* com um enum que identifique o tipo armazenado. Dessa forma, o wrapper sempre sabe como interpretar seu conteúdo.
typedef enum {
TYPE_INT,
TYPE_STRING
} DataType;
typedef struct {
void* data;
DataType type;
} Wrapper;Ao armazenar dados, você aloca memória para o valor real e registra seu tipo. Ao recuperar, você verifica o tipo antes de realizar o 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 padrão é a base para a construção de contêineres genéricos com segurança de tipos em C. O enum atua como uma tag de tipo em tempo de execução, permitindo que seu código tome decisões seguras sobre como lidar com os dados armazenados.
Desafio
FácilVamos construir um sistema de wrapper genérico com segurança de tipo que pode armazenar um número inteiro ou uma string, mantendo o controle de qual tipo ele contém para que possamos recuperar e imprimir os dados com segurança posteriormente.
Você organizará seu código em três arquivos:
wrapper.h: Defina um enumDataTypecom os valoresTYPE_INTeTYPE_STRING. Em seguida, defina uma structWrapperque agrupa um ponteiro de dadosvoid*com um campoDataType. Declare os protótipos das funções para criar wrappers e imprimir seus conteúdos.wrapper.c: Implemente a funcionalidade do wrapper:wrap_int— recebe um inteiro, aloca memória para ele, armazena o valor e retorna um Wrapper com a tag de tipo apropriadawrap_string— recebe uma string (char*), aloca memória e copia a string, e retorna um Wrapper com a tag de tipo de stringprint_wrapper— verifica a tag de tipo e imprime os dados adequadamente (inteiro como está, string como está)free_wrapper— libera a memória alocada dinamicamente dentro do wrapper
main.c: Una tudo. Leia um indicador de tipo (ipara inteiro,spara string) seguido pelo valor. Crie o wrapper apropriado, imprima seu conteúdo e libere a memória.
Seu programa receberá duas entradas:
- Um indicador de tipo:
ipara inteiro ouspara string - O valor a ser envolvido (wrap)
Exemplo de saída quando as entradas são i e 42:
42Exemplo de saída quando as entradas são s e Hello:
HelloExemplo de saída quando as entradas são i e -100:
-100Exemplo de saída quando as entradas são s e Generic Programming:
Generic ProgrammingLembre-se de usar include guards em seu arquivo de cabeçalho. Para cópia de strings, aloque espaço suficiente para a string mais o terminador nulo usando strlen e strcpy.
Folha de consulta
O padrão Generic Wrapper combina um ponteiro void* com informações de tipo para criar contêineres genéricos com segurança de tipos. Isso permite que você armazene qualquer tipo de dado enquanto mantém o conhecimento de qual tipo está armazenado.
Defina um enum para identificar os tipos armazenados:
typedef enum {
TYPE_INT,
TYPE_STRING
} DataType;Crie uma struct que agrupa o ponteiro de dados com a tag de tipo:
typedef struct {
void* data;
DataType type;
} Wrapper;Ao armazenar dados, aloque memória e registre o tipo:
Wrapper wrap_int(int value) {
int* p = malloc(sizeof(int));
*p = value;
return (Wrapper){ .data = p, .type = TYPE_INT };
}Ao recuperar dados, verifique a tag de tipo antes de realizar o 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);
}
}O enum atua como uma tag de tipo em tempo de execução, permitindo a verificação de tipos segura e o tratamento adequado dos dados armazenados.
Experimente você mesmo
#include <stdio.h>
#include <string.h>
#include "wrapper.h"
int main() {
char type;
scanf("%c", &type);
getchar(); // consome a nova linha
Wrapper w;
if (type == 'i') {
int value;
scanf("%d", &value);
// TODO: Criar um wrapper de inteiro usando wrap_int
} else if (type == 's') {
char str[256];
fgets(str, sizeof(str), stdin);
// Remove a nova linha residual, se presente
str[strcspn(str, "\n")] = '\0';
// TODO: Criar um wrapper de string usando wrap_string
}
// TODO: Imprimir o conteúdo do wrapper usando print_wrapper
// TODO: Liberar a memória do wrapper usando free_wrapper
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Object Oriented Programming
1Fundamentos da Programação Modular
Arquivos de CabeçalhoInclude GuardsArquivos-FonteFunções EstáticasRecapitulação: Calculadora Modular4Encapsulamento
Conceito de Ponteiros OpacosDefinindo Structs OpacasGetters e SettersValidação em SettersRecapitulação: Caixa Secreta7Ponteiros para Funções
Declarando Ponteiros para FunçõesChamando Ponteiros para FunçõesTypedef para Ponteiros para FunçõesPassando Funções como ArgumentosRecapitulação: Dispatch de Calculadora10Contêineres Genéricos
Revisão de Ponteiros VoidWrapper GenéricoTroca GenéricaComparação GenéricaRevisão: Array Genérico2Objetos e Métodos
Structs como ObjetosO Ponteiro 'Self'Const CorrectnessPonteiro vs ValorMétodos AuxiliaresRecapitulação: Point Manager5Projeto: Conta Bancária Simples
Configuração do ProjetoImplementação da Conta3Ciclo de Vida de Objetos
Padrão de ConstrutorPadrão de DestrutorInicialização na StackCópia ProfundaRecapitulação: String Wrapper6Herança via Composição
Incorporação de StructsA Regra do Primeiro MembroAcessando Membros PaiUpcastingRecapitulação: Hierarquia de Formas9Projeto: Desenhador de Formas
Visão Geral do ProjetoImplementação do Círculo