Invólucro genérico
Parte da seção Programação Orientada a Objetos do Journey de C da Coddy. Lição 50 de 61.
Um void* sozinho pode armazenar qualquer dado, mas tem uma falha crítica: você perde o controle de qual tipo ele contém. Para criar contêineres genéricos realmente úteis, precisamos combinar o ponteiro de dados com informações de tipo. É aí que entra o padrão Wrapper Genérico.
A ideia é simples: criar 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 em si e registra seu tipo. Ao recuperá-los, você verifica o tipo antes de fazer a conversão:
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);
}
}Esse padrão é a base para criar contêineres genéricos seguros quanto aos tipos em C. O enum funciona como uma etiqueta 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 criar um sistema genérico de wrappers com segurança de tipos, capaz de armazenar um inteiro ou uma string e de acompanhar o tipo armazenado para que possamos recuperar e imprimir os dados com segurança mais tarde.
Você organizará seu código em três arquivos:
wrapper.h: Defina um enumDataTypecom os valoresTYPE_INTeTYPE_STRING. Em seguida, defina uma structWrapperque agrupe um ponteiro de dadosvoid*com um campoDataType. Declare os protótipos das funções para criar wrappers e imprimir seu conteúdo.wrapper.c: Implemente a funcionalidade dos wrappers:wrap_int: recebe um inteiro, aloca memória para ele, armazena o valor e retorna um Wrapper com a etiqueta de tipo apropriadawrap_string: recebe uma string (char*), aloca memória e copia a string, e retorna um Wrapper com a etiqueta de tipo de stringprint_wrapper: verifica a etiqueta de tipo e imprime os dados adequadamente (o inteiro como está, a string como está)free_wrapper: libera os dados alocados dinamicamente dentro do wrapper
main.c: Junte tudo. Leia um indicador de tipo (ipara inteiro,spara string), seguido do 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 armazenado no wrapper
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 guardas de inclusão no arquivo de cabeçalho. Para copiar strings, aloque espaço suficiente para a string mais o terminador nulo usando strlen e strcpy.
Experimente você mesmo
#include <stdio.h>
#include <string.h>
#include "wrapper.h"
int main() {
char type;
scanf("%c", &type);
getchar(); // consome a quebra de linha
Wrapper w;
if (type == 'i') {
int value;
scanf("%d", &value);
// TODO: Crie um wrapper de inteiro usando wrap_int
} else if (type == 's') {
char str[256];
fgets(str, sizeof(str), stdin);
// Remova a quebra de linha final, se houver
str[strcspn(str, "\n")] = '\0';
// TODO: Crie um wrapper de string usando wrap_string
}
// TODO: Imprima o conteúdo do wrapper usando print_wrapper
// TODO: Libere 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 Programação Orientada a Objetos
1Fundamentos da Programação Modular
Arquivos de cabeçalhoProteções de inclusãoArquivos-fonteFunções estáticasRevisão: calculadora modular4Encapsulamento
Conceito de ponteiros opacosDefinindo structs opacasGetters e settersValidação em settersRevisão: caixa secreta7Ponteiros para Funções
Declarando Ponteiros para FunçõesChamando Ponteiros para FunçõesTypedef para Ponteiros para FunçõesPassando Funções como ArgumentosRevisão: Despacho da Calculadora10Contêineres Genéricos
Revisão de ponteiros voidInvólucro genéricoTroca genéricaComparação genéricaRevisão: array genérico2Objetos e métodos
Structs como objetosO ponteiro 'Self'Correção de constPonteiro vs. valorMétodos auxiliaresRevisão: Gerenciador de pontos5Projeto: Conta Bancária Simples
Configuração do ProjetoImplementação da Conta3Ciclo de vida do objeto
Padrão de construtorPadrão de destrutorInicialização da pilhaCópia profundaRevisão: wrapper de String6Herança por composição
Incorporação de structRegra do primeiro membroAcessando membros do tipo paiUpcastingRevisão: hierarquia de formas9Projeto: Desenhador de Formas
Visão geral do projetoImplementação do círculoImplementação do retânguloUso polimórficoContêiner de formasPratique por conta própria: Compilador de C online