Menu
Coddy logo textTech

Обобщенная обертка

Часть раздела Объектно-ориентированное программирование путешествия по C на Coddy. Урок 50 из 61.

Одного void* достаточно, чтобы хранить любые data, но у него есть критический недостаток: вы теряете представление о том, какой тип он содержит. Чтобы создавать действительно полезные универсальные контейнеры, нам нужно объединить указатель на data с информацией о типе. Здесь на помощь приходит паттерн Generic Wrapper.

Идея проста: создайте struct, который объединяет void* с enum, определяющим сохранённый тип. Таким образом, оболочка всегда знает, как интерпретировать свои contents.

typedef enum {
    TYPE_INT,
    TYPE_STRING
} DataType;

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

При хранении data вы выделяете memory для фактического значения и записываете его тип. При извлечении вы проверяете тип перед приведением:

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

Этот шаблон является основой для создания типобезопасных универсальных контейнеров в C. enum действует как тег типа времени выполнения, позволяя вашему коду безопасно принимать решения о том, как обрабатывать сохранённые данные.

challenge icon

Задание

Легко

Давайте создадим типобезопасную Generic систему оболочек, которая может хранить либо integer, либо string, отслеживая, какой тип она содержит, чтобы мы могли безопасно извлечь и вывести data позже.

Вы организуете свой код в трёх файлах:

  • wrapper.h: Define перечисление DataType со значениями TYPE_INT и TYPE_STRING. Затем Define структуру Wrapper, которая объединяет указатель void* на data с полем типа DataType. Declare прототипы function для создания оболочек и вывода их contents.
  • wrapper.c: Implement функциональность оболочки:
    • wrap_int: принимает integer, выделяет для него memory, сохраняет значение и возвращает Wrapper с подходящей меткой типа
    • wrap_string: принимает string (char*), выделяет memory и копирует string, а затем возвращает Wrapper с меткой типа string
    • print_wrapper: проверяет метку типа и соответствующим образом выводит data (integer как есть, string как есть)
    • free_wrapper: освобождает dynamicaly выделенные data внутри оболочки
  • main.c: Объедините всё вместе. Считайте индикатор типа (i для integer, s для string), за которым следует значение. Создайте подходящую оболочку, выведите её 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-терминатором, используя 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