Generischer Wrapper
Teil des Abschnitts Objektorientierte Programmierung der C-Journey von Coddy. Lektion 50 von 61.
Ein void* allein kann beliebige Daten speichern, hat aber einen entscheidenden Nachteil: Du verlierst den Überblick darüber, welchen Typ es enthält. Um wirklich nützliche generische Container zu erstellen, müssen wir den Datenzeiger mit Typinformationen verknüpfen. Hier kommt das Muster des generischen Wrappers ins Spiel.
Die Idee ist einfach: Erstelle eine Struktur, die einen void* mit einem Enum bündelt, das den gespeicherten Typ identifiziert. Auf diese Weise weiß der Wrapper immer, wie sein Inhalt interpretiert werden soll.
typedef enum {
TYPE_INT,
TYPE_STRING
} DataType;
typedef struct {
void* data;
DataType type;
} Wrapper;Beim Speichern von Daten reservierst du Speicher für den tatsächlichen Wert und zeichnest seinen Typ auf. Beim Abrufen überprüfst du den Typ vor der Typumwandlung:
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);
}
}Dieses Muster bildet die Grundlage für die Erstellung typsicherer generischer Container in C. Das Enum fungiert als Laufzeittypkennzeichnung und ermöglicht es deinem Code, sicher zu entscheiden, wie die gespeicherten Daten verarbeitet werden sollen.
Aufgabe
EinfachErstelle ein typsicheres generisches Wrapper-System, das entweder eine Ganzzahl oder eine Zeichenfolge speichern kann und dabei den enthaltenen Typ verfolgt, damit wir die Daten später sicher abrufen und ausgeben können.
Organisiere deinen Code über drei Dateien:
wrapper.h: Definiere einDataType-Enum mit den WertenTYPE_INTundTYPE_STRING. Definiere anschließend eineWrapper-Struktur, die einenvoid*-Datenzeiger mit einemDataType-Feld bündelt. Deklariere die Funktionsprototypen zum Erstellen von Wrappern und zur Ausgabe ihrer Inhalte.wrapper.c: Implementiere die Wrapper-Funktionalität:wrap_int: nimmt eine Ganzzahl entgegen, reserviert dafür Speicher, speichert den Wert und gibt einen Wrapper mit dem passenden Typkennzeichen zurückwrap_string: nimmt eine Zeichenfolge (char*) entgegen, reserviert Speicher, kopiert die Zeichenfolge und gibt einen Wrapper mit dem Typkennzeichen für Zeichenfolgen zurückprint_wrapper: überprüft das Typkennzeichen und gibt die Daten entsprechend aus (Ganzzahl unverändert, Zeichenfolge unverändert)free_wrapper: gibt die dynamisch reservierten Daten innerhalb des Wrappers frei
main.c: Führe alles zusammen. Lies eine Typanzeige (ifür Ganzzahl,sfür Zeichenfolge), gefolgt vom Wert. Erstelle den passenden Wrapper, gib seinen Inhalt aus und gib den Speicher frei.
Dein Programm erhält zwei Eingaben:
- Eine Typanzeige:
ifür Ganzzahl odersfür Zeichenfolge - Der zu verpackende Wert
Beispielausgabe, wenn die Eingaben i und 42 sind:
42Beispielausgabe, wenn die Eingaben s und Hello sind:
HelloBeispielausgabe, wenn die Eingaben i und -100 sind:
-100Beispielausgabe, wenn die Eingaben s und Generic Programming sind:
Generic ProgrammingDenke daran, Include-Guards in deiner Header-Datei zu verwenden. Reserviere zum Kopieren von Zeichenfolgen mit strlen und strcpy ausreichend Speicher für die Zeichenfolge plus das Nullterminierungszeichen.
Probier es selbst
#include <stdio.h>
#include <string.h>
#include "wrapper.h"
int main() {
char type;
scanf("%c", &type);
getchar(); // Zeilenumbruch verbrauchen
Wrapper w;
if (type == 'i') {
int value;
scanf("%d", &value);
// TODO: Erstelle einen Integer-Wrapper mit wrap_int
} else if (type == 's') {
char str[256];
fgets(str, sizeof(str), stdin);
// Entferne abschließenden Zeilenumbruch, falls vorhanden
str[strcspn(str, "\n")] = '\0';
// TODO: Erstelle einen String-Wrapper mit wrap_string
}
// TODO: Gib den Wrapper-Inhalt mit print_wrapper aus
// TODO: Gib den Wrapper-Speicher mit free_wrapper frei
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der modularen Programmierung
Header-DateienInclude GuardsQuelldateienStatische FunktionenWiederholung: Modularer Taschenrechner4Kapselung
Konzept der Opaque PointersOpaque Structs definierenGetter und SetterValidierung in SetternRückblick: Die geheime Box7Funktionszeiger
Funktionszeiger deklarierenFunktionszeiger aufrufenTypedef für FunktionszeigerFunktionen als Argumente übergebenZusammenfassung: Calculator Dispatch10Generische Container
Wiederholung: Void-PointerGenerischer WrapperGenerisches SwapGenerisches VergleichenWiederholung: Generisches Array2Objekte und Methoden
Structs als ObjekteDer 'Self'-PointerConst-CorrectnessPointer vs. WertHilfsmethodenZusammenfassung: Point Manager5Projekt: Einfaches Bankkonto
Projekt-SetupImplementierung des Kontos3Objekt-Lebenszyklus
Konstruktor-MusterDestruktor-MusterStack-InitialisierungTiefe KopieRückblick: String-Wrapper6Vererbung durch Komposition
Struct-EinbettungDie First-Member-RegelZugriff auf Parent-MemberUpcastingRückblick: Formenhierarchie9Projekt: Formen-Zeichner
ProjektübersichtKreis-ImplementierungRechteck-ImplementierungPolymorphe VerwendungShape-ContainerÜbe selbstständig: Online-C-Compiler