Menu
Coddy logo textTech

Generyczny wrapper

Część sekcji Programowanie obiektowe ścieżki C w Coddy. Lekcja 50 z 61.

Sam wskaźnik void* może przechowywać dowolne dane, ale ma istotną wadę: tracisz informację o tym, jaki typ przechowuje. Aby tworzyć naprawdę użyteczne kontenery generyczne, musimy połączyć wskaźnik do danych z informacją o typie. Właśnie tutaj pojawia się wzorzec Generic Wrapper.

Pomysł jest prosty: utwórz strukturę, która łączy void* z enumem identyfikującym przechowywany typ. Dzięki temu wrapper zawsze wie, jak interpretować swoją zawartość.

typedef enum {
    TYPE_INT,
    TYPE_STRING
} DataType;
typedef struct {
    void* data;
    DataType type;
} Wrapper;

Podczas przechowywania danych przydzielasz pamięć na rzeczywistą wartość i zapisujesz jej typ. Podczas pobierania sprawdzasz typ przed rzutowaniem:

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);
    }
}

Ten wzorzec stanowi podstawę tworzenia bezpiecznych typowo, generycznych kontenerów w C. Wyliczenie pełni funkcję znacznika typu w czasie wykonywania, pozwalając kodowi podejmować bezpieczne decyzje dotyczące obsługi przechowywanych danych.

challenge icon

Wyzwanie

Łatwy

Zbudujmy bezpieczny typowo, generyczny system opakowujący, który może przechowywać liczbę całkowitą albo ciąg znaków, śledząc, jaki typ przechowuje, abyśmy mogli później bezpiecznie pobrać i wypisać dane.

Podzielisz swój kod na trzy pliki:

  • wrapper.h: Zdefiniuj typ wyliczeniowy DataType z wartościami TYPE_INT i TYPE_STRING. Następnie zdefiniuj strukturę Wrapper, która łączy wskaźnik danych void* z polem typu DataType. Zadeklaruj prototypy funkcji tworzących opakowania i wypisujących ich zawartość.
  • wrapper.c: Zaimplementuj funkcjonalność opakowania:
    • wrap_int: przyjmuje liczbę całkowitą, przydziela dla niej pamięć, zapisuje wartość i zwraca obiekt Wrapper z odpowiednią etykietą typu
    • wrap_string: przyjmuje ciąg znaków (char*), przydziela pamięć i kopiuje ciąg, a następnie zwraca obiekt Wrapper z etykietą typu ciągu znaków
    • print_wrapper: sprawdza etykietę typu i odpowiednio wypisuje dane (liczbę całkowitą bez zmian, ciąg znaków bez zmian)
    • free_wrapper: zwalnia dynamicznie przydzielone dane znajdujące się w opakowaniu
  • main.c: Połącz wszystko w całość. Odczytaj wskaźnik typu (i dla liczby całkowitej, s dla ciągu znaków), a następnie wartość. Utwórz odpowiednie opakowanie, wypisz jego zawartość i zwolnij pamięć.

Program otrzyma dwa dane wejściowe:

  1. Wskaźnik typu: i dla liczby całkowitej lub s dla ciągu znaków
  2. Wartość do opakowania

Przykładowe wyjście, gdy danymi wejściowymi są i i 42:

42

Przykładowe wyjście, gdy danymi wejściowymi są s i Hello:

Hello

Przykładowe wyjście, gdy danymi wejściowymi są i i -100:

-100

Przykładowe wyjście, gdy danymi wejściowymi są s i Generic Programming:

Generic Programming

Pamiętaj, aby użyć strażników dołączania w pliku nagłówkowym. Przy kopiowaniu ciągu znaków przydziel wystarczającą ilość miejsca na ciąg wraz z terminatorem null, używając strlen i strcpy.

Spróbuj swoich sił

#include <stdio.h>
#include <string.h>
#include "wrapper.h"

int main() {
    char type;
    scanf("%c", &type);
    getchar(); // pobierz znak nowej linii
    
    Wrapper w;
    
    if (type == 'i') {
        int value;
        scanf("%d", &value);
        // TODO: Utwórz obiekt opakowujący liczbę całkowitą za pomocą wrap_int
        
    } else if (type == 's') {
        char str[256];
        fgets(str, sizeof(str), stdin);
        // Usuń końcowy znak nowej linii, jeśli występuje
        str[strcspn(str, "\n")] = '\0';
        // TODO: Utwórz obiekt opakowujący ciąg znaków za pomocą wrap_string
        
    }
    
    // TODO: Wypisz zawartość obiektu opakowującego za pomocą print_wrapper
    
    // TODO: Zwolnij pamięć obiektu opakowującego za pomocą free_wrapper
    
    return 0;
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator C online