Wrapper générique
Fait partie de la section Object Oriented Programming du Journey C de Coddy — leçon 50 sur 61.
Un void* seul peut stocker n'importe quelle donnée, mais il présente un défaut critique : vous perdez la trace du type qu'il contient. Pour construire des conteneurs génériques vraiment utiles, nous devons associer le pointeur de données à des informations de type. C'est là qu'intervient le modèle Generic Wrapper.
L'idée est simple : créer une structure qui regroupe un void* avec une énumération qui identifie le type stocké. De cette façon, le wrapper sait toujours comment interpréter son contenu.
typedef enum {
TYPE_INT,
TYPE_STRING
} DataType;
typedef struct {
void* data;
DataType type;
} Wrapper;Lors du stockage de données, vous allouez de la mémoire pour la valeur réelle et enregistrez son type. Lors de la récupération, vous vérifiez le type avant le transtypage :
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);
}
}Ce modèle est le fondement de la création de conteneurs génériques de type sûr en C. L'enum sert d'étiquette de type à l'exécution, permettant à votre code de prendre des décisions sûres sur la manière de manipuler les données stockées.
Défi
FacileConstruisons un système de wrapper générique de type sécurisé capable de stocker soit un entier, soit une chaîne de caractères, en gardant la trace du type qu'il contient afin de pouvoir récupérer et afficher les données en toute sécurité plus tard.
Vous organiserez votre code sur trois fichiers :
wrapper.h: Définissez une énumérationDataTypeavec les valeursTYPE_INTetTYPE_STRING. Définissez ensuite une structureWrapperqui regroupe un pointeur de donnéesvoid*avec un champDataType. Déclarez les prototypes de fonctions pour créer des wrappers et afficher leur contenu.wrapper.c: Implémentez les fonctionnalités du wrapper :wrap_int— prend un entier, lui alloue de la mémoire, stocke la valeur et renvoie un Wrapper avec la balise de type appropriéewrap_string— prend une chaîne (char*), alloue de la mémoire et copie la chaîne, puis renvoie un Wrapper avec la balise de type chaîneprint_wrapper— vérifie la balise de type et affiche les données de manière appropriée (entier tel quel, chaîne telle quelle)free_wrapper— libère la mémoire allouée dynamiquement à l'intérieur du wrapper
main.c: Rassemblez le tout. Lisez un indicateur de type (ipour entier,spour chaîne) suivi de la valeur. Créez le wrapper approprié, affichez son contenu et libérez la mémoire.
Votre programme recevra deux entrées :
- Un indicateur de type :
ipour un entier ouspour une chaîne - La valeur à envelopper
Exemple de sortie lorsque les entrées sont i et 42 :
42Exemple de sortie lorsque les entrées sont s et Hello :
HelloExemple de sortie lorsque les entrées sont i et -100 :
-100Exemple de sortie lorsque les entrées sont s et Generic Programming :
Generic ProgrammingN'oubliez pas d'utiliser des gardes d'inclusion (include guards) dans votre fichier d'en-tête. Pour la copie de chaînes, allouez suffisamment d'espace pour la chaîne plus le terminateur nul en utilisant strlen et strcpy.
Aide-mémoire
Le patron Generic Wrapper associe un pointeur void* à des informations de type pour créer des conteneurs génériques de type sécurisé. Cela vous permet de stocker n'importe quel type de données tout en conservant la connaissance du type stocké.
Définissez une énumération pour identifier les types stockés :
typedef enum {
TYPE_INT,
TYPE_STRING
} DataType;Créez une structure qui regroupe le pointeur de données avec la balise de type :
typedef struct {
void* data;
DataType type;
} Wrapper;Lors du stockage des données, allouez de la mémoire et enregistrez le type :
Wrapper wrap_int(int value) {
int* p = malloc(sizeof(int));
*p = value;
return (Wrapper){ .data = p, .type = TYPE_INT };
}Lors de la récupération des données, vérifiez la balise de type avant le transtypage :
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);
}
}L'énumération agit comme une balise de type à l'exécution, permettant une vérification de type sûre et une manipulation appropriée des données stockées.
Essayez vous-même
#include <stdio.h>
#include <string.h>
#include "wrapper.h"
int main() {
char type;
scanf("%c", &type);
getchar(); // consommer le saut de ligne
Wrapper w;
if (type == 'i') {
int value;
scanf("%d", &value);
// TODO: Créer un wrapper d'entier en utilisant wrap_int
} else if (type == 's') {
char str[256];
fgets(str, sizeof(str), stdin);
// Supprimer le saut de ligne final s'il est présent
str[strcspn(str, "\n")] = '\0';
// TODO: Créer un wrapper de chaîne en utilisant wrap_string
}
// TODO: Afficher le contenu du wrapper en utilisant print_wrapper
// TODO: Libérer la mémoire du wrapper en utilisant free_wrapper
return 0;
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Object Oriented Programming
1Bases de la programmation modulaire
Fichiers d'en-têteGardes d'inclusionFichiers sourcesFonctions statiquesRécapitulatif : Calculatrice modulaire4Encapsulation
Concept des pointeurs opaquesDéfinir des structures opaquesGetters et settersValidation dans les settersRécapitulatif : La boîte secrète7Pointeurs sur fonctions
Déclaration de pointeurs sur fonctionsAppel de pointeurs sur fonctionsTypedef pour les pointeurs sur fonctionsPassage de fonctions en argumentsRécapitulatif : Dispatcher de calculatrice10Conteneurs génériques
Rappel sur les pointeurs voidWrapper génériqueÉchange génériqueComparaison génériqueRappel : tableaux génériques2Objets et méthodes
Structs comme objetsLe pointeur 'Self'Rigueur du mot-clé constPointeur vs ValeurMéthodes utilitairesRécapitulatif : Point Manager5Projet : Compte bancaire simple
Configuration du projetImplémentation du compte8Polymorphisme
Pointeurs de fonctions dans les structuresSimulation de méthodesLe concept d'interfaceImplémentation d'interfacesItération polymorphiqueRécapitulatif : Greeter11Patrons de conception en C
Patron SingletonPatron FabriquePatron ItérateurRécapitulatif : Logger Factory3Cycle de vie des objets
Pattern de constructeurPattern de destructeurInitialisation sur la pileCopie profondeRécapitulatif : String Wrapper6Héritage par composition
Imbrication de structLa règle du premier membreAccès aux membres parentsUpcastingRécapitulatif : Hiérarchie des formes9Projet : Dessinateur de formes
Aperçu du projetImplémentation du cercle