Menu
Coddy logo textTech

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.

challenge icon

Défi

Facile

Construisons 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ération DataType avec les valeurs TYPE_INT et TYPE_STRING. Définissez ensuite une structure Wrapper qui regroupe un pointeur de données void* avec un champ DataType. 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ée
    • wrap_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îne
    • print_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 (i pour entier, s pour 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 :

  1. Un indicateur de type : i pour un entier ou s pour une chaîne
  2. La valeur à envelopper

Exemple de sortie lorsque les entrées sont i et 42 :

42

Exemple de sortie lorsque les entrées sont s et Hello :

Hello

Exemple de sortie lorsque les entrées sont i et -100 :

-100

Exemple de sortie lorsque les entrées sont s et Generic Programming :

Generic Programming

N'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;
}
quiz iconTestez-vous

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