Jenerik Sarmalayıcı
Coddy'nin C Journey'sinin Object Oriented Programming bölümünün bir parçası — ders 50 / 61.
Tek başına bir void* herhangi bir veriyi saklayabilir, ancak kritik bir kusuru vardır: hangi türü tuttuğunun takibini kaybedersiniz. Gerçekten kullanışlı genel kapsayıcılar oluşturmak için, veri işaretçisini tür bilgisiyle eşleştirmemiz gerekir. İşte burada Generic Wrapper deseni devreye girer.
Fikir basit: Bir void* ile depolanan türü tanımlayan bir enum'u bir araya getiren bir struct oluşturun. Bu sayede, sarmalayıcı (wrapper) içeriğini nasıl yorumlayacağını her zaman bilir.
typedef enum {
TYPE_INT,
TYPE_STRING
} DataType;
typedef struct {
void* data;
DataType type;
} Wrapper;Veri depolarken, gerçek değer için bellek ayırır ve türünü kaydedersiniz. Veriyi geri alırken, tür dönüştürme (casting) işleminden önce türü kontrol edersiniz:
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);
}
}Bu desen, C'de tip güvenli generic kapsayıcılar oluşturmanın temelidir. enum, bir çalışma zamanı tip etiketi görevi görerek kodunuzun saklanan veriyi nasıl işleyeceği konusunda güvenli kararlar vermesini sağlar.
Görev
KolayDaha sonra verileri güvenli bir şekilde alıp yazdırabilmemiz için hangi türü tuttuğunu takip eden, bir tam sayı veya bir dize saklayabilen tür açısından güvenli (type-safe) bir genel sarmalayıcı (generic wrapper) sistemi oluşturalım.
Kodunuzu üç dosya halinde düzenleyeceksiniz:
wrapper.h:TYPE_INTveTYPE_STRINGdeğerlerine sahip birDataTypeenum tanımlayın. Ardından, birvoid*veri göstericisi ile birDataTypealanını birleştiren birWrapperstruct tanımlayın. Sarmalayıcılar oluşturmak ve içeriklerini yazdırmak için fonksiyon prototiplerini bildirin.wrapper.c: Sarmalayıcı işlevselliğini uygulayın:wrap_int— bir tam sayı alır, onun için bellek ayırır, değeri saklar ve uygun tür etiketiyle bir Wrapper döndürürwrap_string— bir dize (char*) alır, bellek ayırır ve dizeyi kopyalar ve dize türü etiketiyle bir Wrapper döndürürprint_wrapper— tür etiketini kontrol eder ve verileri uygun şekilde yazdırır (tam sayı olduğu gibi, dize olduğu gibi)free_wrapper— sarmalayıcı içindeki dinamik olarak ayrılmış belleği serbest bırakır
main.c: Hepsini bir araya getirin. Bir tür göstergesi (tam sayı içini, dize içins) ve ardından değeri okuyun. Uygun sarmalayıcıyı oluşturun, içeriğini yazdırın ve belleği serbest bırakın.
Programınız iki girdi alacaktır:
- Bir tür göstergesi: tam sayı için
iveya dize içins - Sarmalanacak değer
Girdiler i ve 42 olduğunda örnek çıktı:
42Girdiler s ve Hello olduğunda örnek çıktı:
HelloGirdiler i ve -100 olduğunda örnek çıktı:
-100Girdiler s ve Generic Programming olduğunda örnek çıktı:
Generic ProgrammingBaşlık dosyanızda include guard kullanmayı unutmayın. Dize kopyalama için, strlen ve strcpy kullanarak dize artı null sonlandırıcı için yeterli alan ayırın.
Kopya kağıdı
Generic Wrapper (Genel Sarmalayıcı) deseni, tür açısından güvenli genel kapsayıcılar oluşturmak için bir void* işaretçisini tür bilgisiyle eşleştirir. Bu, hangi türün saklandığı bilgisini korurken herhangi bir veri türünü saklamanıza olanak tanır.
Saklanan türleri tanımlamak için bir enum tanımlayın:
typedef enum {
TYPE_INT,
TYPE_STRING
} DataType;Veri işaretçisini tür etiketiyle birleştiren bir struct oluşturun:
typedef struct {
void* data;
DataType type;
} Wrapper;Veri saklarken, bellek ayırın ve türü kaydedin:
Wrapper wrap_int(int value) {
int* p = malloc(sizeof(int));
*p = value;
return (Wrapper){ .data = p, .type = TYPE_INT };
}Veriyi geri alırken, tür dönüşümü (casting) yapmadan önce tür etiketini kontrol edin:
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);
}
}Enum, çalışma zamanı tür etiketi (runtime type tag) görevi görerek güvenli tür kontrolü ve saklanan verilerin uygun şekilde işlenmesini sağlar.
Kendin dene
#include <stdio.h>
#include <string.h>
#include "wrapper.h"
int main() {
char type;
scanf("%c", &type);
getchar(); // yeni satır karakterini tüket
Wrapper w;
if (type == 'i') {
int value;
scanf("%d", &value);
// TODO: wrap_int kullanarak bir tamsayı (integer) sarmalayıcı oluşturun
} else if (type == 's') {
char str[256];
fgets(str, sizeof(str), stdin);
// Eğer varsa sondaki yeni satır karakterini kaldırın
str[strcspn(str, "\n")] = '\0';
// TODO: wrap_string kullanarak bir dize (string) sarmalayıcı oluşturun
}
// TODO: print_wrapper kullanarak sarmalayıcı içeriğini yazdırın
// TODO: free_wrapper kullanarak sarmalayıcı belleğini serbest bırakın
return 0;
}
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Object Oriented Programming bölümündeki tüm dersler
1Modüler Programlama Temelleri
Başlık DosyalarıInclude Guard'larKaynak DosyalarıStatik FonksiyonlarÖzet: Modüler Hesap Makinesi4Kapsülleme
Opaque Pointer KavramıOpaque Struct TanımlamaGetter ve Setter MetotlarıSetter Metotlarında DoğrulamaÖzet: Gizli Kutu7Fonksiyon İşaretçileri
Fonksiyon İşaretçilerini TanımlamaFonksiyon İşaretçilerini ÇağırmaFonksiyon İşaretçileri için TypedefFonksiyonları Argüman Olarak GeçirmeÖzet: Hesap Makinesi Yönlendirme10Jenerik Kapsayıcılar
Void İşaretçiler ÖzetiJenerik SarmalayıcıJenerik Yer DeğiştirmeJenerik KarşılaştırmaÖzet: Jenerik Dizi2Nesneler ve Metotlar
Nesne Olarak Struct'lar'Self' İşaretçisiConst Doğruluğuİşaretçi vs DeğerYardımcı MetotlarÖzet: Point Manager5Proje: Basit Banka Hesabı
Proje KurulumuHesabın Oluşturulması3Nesne Yaşam Döngüsü
Constructor DeseniDestructor DeseniStack İlklendirmeDerin KopyalamaÖzet: String Wrapper6Kompozisyon Yoluyla Kalıtım
Struct Gömmeİlk Üye KuralıÜst Üyelere ErişimUpcastingÖzet: Şekil Hiyerarşisi