Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

مغلف عام

جزء من قسم البرمجة كائنية التوجه في رحلة C على Coddy. الدرس 50 من 61.

يمكن لـ void* وحده تخزين أي بيانات، لكنه يعاني من عيب جوهري: تفقد معرفة نوع البيانات التي يحتوي عليها. ولإنشاء حاويات Generic مفيدة حقًا، نحتاج إلى إقران pointer البيانات بمعلومات النوع. وهنا يأتي دور نمط Generic Wrapper.

الفكرة بسيطة: أنشئ struct يجمع بين void* وenum يحدد النوع المخزّن. بهذه الطريقة، يعرف الغلاف دائمًا كيفية تفسير contents.

typedef enum {
    TYPE_INT,
    TYPE_STRING
} DataType;

typedef struct {
    void* data;
    DataType type;
} Wrapper;

عند تخزين البيانات، تقوم بتخصيص الذاكرة للقيمة الفعلية وتسجّل نوعها. وعند استردادها، تتحقق من النوع قبل إجراء التحويل:

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

هذا النمط هو الأساس لبناء حاويات generic آمنة من حيث النوع في لغة C. يعمل enum كوسم نوع وقت التشغيل، مما يتيح لكودك اتخاذ قرارات آمنة حول كيفية التعامل مع data المخزنة.

challenge icon

التحدي

سهل

لنَبْنِ نظام تغليف Generic آمن من حيث النوع يمكنه تخزين إما قيمة integer أو قيمة string، مع تتبّع نوع القيمة التي يحتوي عليها حتى نتمكن من استرجاع data وطباعتها بأمان لاحقًا.

ستنظّم الكود عبر ثلاثة ملفات:

  • wrapper.h: عرّف تعداد DataType بالقيم TYPE_INT وTYPE_STRING. ثم عرّف بنية Wrapper التي تجمع مؤشر data من النوع void* مع field من النوع DataType. صرّح بـ function prototypes الخاصة بإنشاء الأغلفة وطباعة contents الخاصة بها.
  • wrapper.c: Implement وظيفة التغليف:
    • wrap_int: تستقبل integer، وتخصص memory له، وتخزن القيمة، ثم تعيد Wrapper مع وسم النوع appropriately
    • wrap_string: تستقبل string ‏(char*)، وتخصص memory وتنسخ string، ثم تعيد Wrapper مع وسم نوع string
    • print_wrapper: تتحقق من وسم النوع وتطبع data appropriately (integer كما هي، وstring كما هي)
    • free_wrapper: تحرر data المخصصة dynamically داخل الغلاف
  • main.c: اجمع كل ذلك معًا. اقرأ مؤشر نوع (i لـ integer أو s لـ string) متبوعًا بالقيمة. Create الغلاف appropriately، واطبع contents الخاصة به، ثم حرر memory.

سيستقبل برنامجك مدخلين:

  1. مؤشر نوع: i لـ integer أو s لـ string
  2. القيمة المراد تغليفها

مثال على المخرجات عندما تكون المدخلات i و42:

42

مثال على المخرجات عندما تكون المدخلات s وHello:

Hello

مثال على المخرجات عندما تكون المدخلات i و-100:

-100

مثال على المخرجات عندما تكون المدخلات s وGeneric Programming:

Generic Programming

تذكّر استخدام include guards في ملف الترويسة. لنسخ string، خصص مساحة كافية لـ string بالإضافة إلى null terminator باستخدام strlen وstrcpy.

جرّب بنفسك

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

int main() {
    char type;
    scanf("%c", &type);
    getchar(); // استهلك السطر الجديد
    
    Wrapper w;
    
    if (type == 'i') {
        int value;
        scanf("%d", &value);
        // TODO: أنشئ غلاف عدد صحيح باستخدام wrap_int
        
    } else if (type == 's') {
        char str[256];
        fgets(str, sizeof(str), stdin);
        // أزل السطر الجديد اللاحق إن وُجد
        str[strcspn(str, "\n")] = '\0';
        // TODO: أنشئ غلاف سلسلة باستخدام wrap_string
        
    }
    
    // TODO: اطبع محتويات الغلاف باستخدام print_wrapper
    
    // TODO: حرر ذاكرة الغلاف باستخدام free_wrapper
    
    return 0;
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم C عبر الإنترنت