Wrapper generico
Fa parte della sezione Programmazione orientata agli oggetti del percorso C di Coddy. Lezione 50 di 61.
Un void* da solo può memorizzare qualsiasi dato, ma ha un difetto critico: perdi traccia del tipo che contiene. Per creare contenitori generici davvero utili, dobbiamo associare il puntatore ai dati alle informazioni sul tipo. È qui che entra in gioco il pattern Generic Wrapper.
L’idea è semplice: crea una struct che raggruppa un void* con un enum che identifica il tipo memorizzato. In questo modo, il wrapper sa sempre come interpretarne il contenuto.
typedef enum {
TYPE_INT,
TYPE_STRING
} DataType;
typedef struct {
void* data;
DataType type;
} Wrapper;Quando memorizzi i dati, allochi memoria per il valore effettivo e ne registri il tipo. Quando li recuperi, controlli il tipo prima di eseguire il cast:
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);
}
}Questo schema è la base per creare contenitori generici e sicuri rispetto ai tipi in C. L'enum funge da tag di tipo in fase di esecuzione, permettendo al tuo codice di prendere decisioni sicure su come gestire i dati memorizzati.
Sfida
FacileCreiamo un sistema generico di wrapper type-safe in grado di memorizzare un intero oppure una stringa, tenendo traccia del tipo contenuto così da poter recuperare e stampare i dati in sicurezza in seguito.
Organizzerai il codice in tre file:
wrapper.h: Definisci un'enumDataTypecon i valoriTYPE_INTeTYPE_STRING. Poi definisci una structWrapperche raggruppi un puntatore ai dativoid*con un campoDataType. Dichiara i prototipi delle funzioni per creare i wrapper e stamparne il contenuto.wrapper.c: Implementa le funzionalità del wrapper:wrap_int: accetta un intero, ne alloca la memoria, memorizza il valore e restituisce un Wrapper con il tag del tipo appropriatowrap_string: accetta una stringa (char*), alloca memoria e copia la stringa, quindi restituisce un Wrapper con il tag del tipo stringaprint_wrapper: controlla il tag del tipo e stampa i dati nel modo appropriato (l'intero così com'è, la stringa così com'è)free_wrapper: libera i dati allocati dinamicamente all'interno del wrapper
main.c: Metti tutto insieme. Leggi un indicatore di tipo (iper l'intero,sper la stringa) seguito dal valore. Crea il wrapper appropriato, stampane il contenuto e libera la memoria.
Il programma riceverà due input:
- Un indicatore di tipo:
iper un intero oppuresper una stringa - Il valore da racchiudere nel wrapper
Esempio di output quando gli input sono i e 42:
42Esempio di output quando gli input sono s e Hello:
HelloEsempio di output quando gli input sono i e -100:
-100Esempio di output quando gli input sono s e Generic Programming:
Generic ProgrammingRicordati di usare le include guard nel file header. Per copiare la stringa, alloca spazio sufficiente per la stringa più il terminatore nullo usando strlen e strcpy.
Provalo tu
#include <stdio.h>
#include <string.h>
#include "wrapper.h"
int main() {
char type;
scanf("%c", &type);
getchar(); // consuma il carattere di nuova riga
Wrapper w;
if (type == 'i') {
int value;
scanf("%d", &value);
// TODO: Crea un wrapper per un intero usando wrap_int
} else if (type == 's') {
char str[256];
fgets(str, sizeof(str), stdin);
// Rimuovi il carattere di nuova riga finale, se presente
str[strcspn(str, "\n")] = '\0';
// TODO: Crea un wrapper per una stringa usando wrap_string
}
// TODO: Stampa il contenuto del wrapper usando print_wrapper
// TODO: Libera la memoria del wrapper usando free_wrapper
return 0;
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Basi di programmazione modulare
File di intestazioneGuardie di inclusioneFile sorgenteFunzioni staticheRipasso: calcolatrice modulare4Incapsulamento
Il concetto di puntatori opachiDefinire struct opacheGetter e setterLa convalida nei setterRiepilogo: scatola segreta7Puntatori a funzione
Dichiarare puntatori a funzioneChiamare i puntatori a funzioneTypedef per puntatori a funzionePassare funzioni come argomentiRiepilogo: dispatch della calcolatrice10Contenitori generici
Riepilogo dei puntatori voidWrapper genericoScambio genericoConfronto genericoRiepilogo: array generico2Oggetti e metodi
Le struct come oggettiIl puntatore 'Self'Correttezza constPuntatore o valoreMetodi di supportoRiepilogo: gestore di punti5Progetto: Conto bancario semplice
Configurazione del progettoImplementazione del conto8Polimorfismo
Puntatori a funzione nelle structSimulare i metodiIl concetto di interfacciaImplementare le interfacceIterazione polimorficaRiepilogo: Greeter11Pattern di progettazione in C
Pattern SingletonPattern FactoryPattern IteratorRiepilogo: Factory di Logger3Ciclo di vita degli oggetti
Pattern del costruttorePattern del distruttoreInizializzazione sullo stackCopia profondaRiepilogo: wrapper di stringhe6Ereditarietà tramite composizione
Incorporamento delle structLa regola del primo membroAccesso ai membri della classe baseUpcastingRiepilogo: gerarchia di forme9Progetto: Disegnatore di forme
Panoramica del progettoImplementazione del cerchioImplementazione del rettangoloUtilizzo polimorficoContenitore di formeEsercitati da solo: Compilatore C online