Intereting Posts
строковый литерал в c ищет функцию, которая ничего не делает / ждет Повреждения сервера Socket Получение собственного внешнего IP-адреса в POSIX C Как объявить переменную в области данной функции с помощью GDB? Библиотека Linux Pthread, аргументы streamа Как быть предупрежденным о указателях для локальных переменных вне зоны видимости Какой лучший компилятор C для семейства 8051? C: как двойной номер (например, 123.45) хранится в переменной float или двойной переменной или двойной двойной переменной? Ярлык для запоминания порядка оценки и приоритета операторов в C Глобальная внешняя константа C ++, определенная во время выполнения, доступная для нескольких исходных файлов Странное поведение пустого символа в регулярном выражении C Почему Swift в этом тесте обработки изображений в 100 раз медленнее C? Определить символ EOF LCC: Инициализация структур, содержащих структуры?

Лучший подход для структурного polymorphismа в C

Я пишу простой двухмерный векторный объект. Он будет иметь компоненты x и y, а также длину, перекрестное произведение и т. Д. Дело в том, что я хочу, чтобы структура имела много возможных типов (char, int, float, double и т. Д.). Мне было интересно, какой будет лучший выбор, дизайн мудрый, чтобы взаимодействовать с объектом? Вот что я сейчас рассматриваю:

1. Попросите пользователя передать векторный объект специализированным функциям, например:

Vector2Dt_Dot(Vec2Dt* vector1, Vec2Dt* vector2); 

где ‘t’ – тип вектора. Однако проблема с этим подходом заключается в том, что он не позволяет различным типам взаимодействовать друг с другом, поэтому я не мог рассчитать вычисление точечного произведения float vector2d и double vector2d. Второй подход и то, к чему я склоняюсь:

2. Попросите пользователя передать векторный объект (ы) как указатели void вместе с их типами, например:

 Vector2D_Dot(void* vector1, unsigned vector1_type, void* vector2, unsigned vector2_type); 

очевидно, что этот подход более компактен по API, а также решает вышеупомянутую проблему, но ценой нескольких дополнительных параметров и типа безопасности.

Могут быть другие решения, о которых я не знаю, однако это те, которые я сейчас рассматриваю. Что вы чувствуете, это лучший подход к этому?

Вы можете использовать полиморфные объекты. Определите структуры следующим образом:

 #define INT_TYPE 0 #define DOUBLE_TYPE 1 //more type constants typedef struct Vector2D { int type; } Vector2D; typedef struct Vector2D_int { Vector2D super; int x, y; } Vector2D_int; typedef struct Vector2D_double { Vector2D super; double x, y; } Vector2D_double; //more typed vector structures 

Затем вы можете написать свои функции, чтобы принять указатели Vector2D , проверить их соответствующие поля типов и отбросить их до соответствующего типизированного варианта для доступа к данным полезной нагрузки.

 double Vector2D_length(const Vector2D* vector) { if(vector->type == TYPE_INT) { const Vector2D_int* intVector = (Vector2D_int*)vector; return sqrt(intVector->x * intVector->x + intVector->y * intVector->y); } if(vector->type == TYPE_DOUBLE) { const Vector2D_double* doubleVector = (Vector2D_double*)vector; return sqrt(doubleVector->x * doubleVector->x + doubleVector->y * doubleVector->y); } //other cases follow } 

Это polymorphism, закодированный вручную. Все, что вам нужно, чтобы убедиться, что поле type всегда установлено на правильное значение (устанавливается один раз при создании типизированного вектора).

Преимущество этого подхода к вашей второй идее заключается в том, что вам не нужно передавать тип векторов в другую переменную, которая будет делать ваши векторы утомительными и подверженными ошибкам.

В качестве альтернативы вы можете определить свое поле type чтобы он содержал указатель на структуру указателей функций. Вы должны создать один объект этой структуры указателя этой функции на определенный тип Vector, который вы определяете, и использовать его для поиска, какую функцию использовать с данным вектором. Этот подход будет очень близок к тому, что делает C ++ под капотом.

Вы можете использовать список переменных аргументов, его прототип закодирован, например:

 int xyz(int a, ...); 

Этот стиль нуждается в одном определенном параметре в этом примере a , за которым следует любое количество параметров, типы данных которых могут быть определены во время выполнения.

См. Функции и объекты: va_list ; va_start ; va_args ; и va_end , для полного описания того, как обрабатывать списки аргументов переменных.

Надеюсь это поможет. Если у вас есть вопросы о va_list и т. Д., Пожалуйста, спросите.

На самом деле я поступил следующим образом:

Я создал базовый class Vector2D со следующим расположением:

 struct Vector2D_Base; typedef struct Vector2D_Base{ M_double (*Vector2D_Get_X)(struct Vector2D_Base* vec); M_double (*Vector2D_Get_Y)(struct Vector2D_Base* vec); } Vector2D; 

Как вы можете видеть, это позволяет универсальным векторным функциям вызывать их, чтобы получить значения производных classов «x» и «y», преобразованные в парные, что не позволяет общей функции не беспокоиться о различии между типами типов, такими как char и float. Затем каждый производный class:

 #define DEFINE_VECTOR2D(type, name)\ typedef struct{\ Vector2D_Base vec_base;\ type x, y;\ } Vector2D##name\