LINUX.ORG.RU

Полиморфизм в Си

 


0

3

Всегда были проблемы с такого рода проектированием.

Вот пример:

struct ca_str {
  char n;
};

struct cb_str {
  int n;
};

struct p_str {
  struct c_str *c;
  /*must be a link to either
    struct ca_str or struct cb_str
  */
};

Нужно что бы struct p_str содержала указатель или на struct ca_str, или же на struct cb_str. Из моих изобретений – можно держать указатели на все возможные структуры. Все ненужные из них делать тотальными NULL.

Но как это делают программисты в программировании?

Перемещено leave из general

tagged union

anonymous
()

В Си полиморфизм делается исключительно дендрофекальным методом (из говна и палок). А значит, какой способ не выбери — все они <аксиома Эскобара>

XMs ★★★★★
()
struct ca_str {
  char n;
};

struct cb_str {
  int n;
};

union c_str_ptr {
    struct ca_str *a;
    struct cb_str *b;
};

struct p_str {
    union c_str_ptr *c;
};
Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

Спасибо! В моём учебнике про такое полезное применение union’ов не упоминалось.

yufhgigibi
() автор топика

либо так

struct p_str {
  union {
    struct ca_str *ca;
    struct cb_str *cb;
  };
};

struct p_str p; /* объявляем переменную */

либо так

union p_str {
  struct ca_str *ca;
  struct cb_str *cb;
};

union p_str p; /* объявляем переменную */

Во втором случае придётся тип называть не struct а union при объявлении переменных. Ну а ещё лучше так:

typedef union {
  struct ca_str *ca;
  struct cb_str *cb;
} p_str;

p_str p; /* объявляем переменную */

незачем таскать эти struct/union рядом с типом везде, замусоривая вид кода, и typedef позволяет назвать тип одним словом

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)

Что за фигня. Какие union из ссылок?

Внешне, должно быть +/- так:

struct animal {
  // ...
};

void animal_say(struct animal *animal);
void animal_free(struct animal *animal);

struct cat {
  struct animal base;
  // ...
};

struct animal* cat_new(/*...*/);

struct dog {
  struct animal base;
  // ...
};

struct animal* dog_new(/*...*/);

int main() {
  
  struct animal *animals[2] = { cat_new(), dog_new() };
  animal_say(animals[0]);
  animal_say(animals[1]);
  animal_free(animals[0]);
  animal_free(animals[1]);
  return 0;
}
AlexVR ★★★★★
()
Последнее исправление: AlexVR (всего исправлений: 1)
Ответ на: комментарий от AlexVR

Там нет никаких base, это полностью разные типы. Хотя автор конечно не до конца объяснил зачем ему это всё, но сразу сувать ему ООП-приёмы ни к чему.

firkax ★★★★★
()
Ответ на: комментарий от firkax

Так ТС хотел полиморфизм?

Хотя автор конечно не до конца объяснил зачем ему это всё

Вот это пожалуй главное.

AlexVR ★★★★★
()

Так это же СУВТ из метапрога. В нем это устроено так:

struct ContainerType {
  enum Key {
    TYPE_A,
    TYPE_B,
    TYPE_C
  } key;
  union {
    TypeA a;
    TypeB b;
    TypeC c;
  };
};

// Использование
ContainerType suvt = { 
  .key = TYPE_A,
  .a = CreateTypeA()
};

...
switch(suvt.key) {
case TYPE_A:
  return suvt.a.value+'A';
case TYPE_B:
  return suvt.b.value+'B';
...
}

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)

И какое это имеет отношение к полиморфизму? Хочешь играть в ООП, пихай указатели на функции обрабатывающие тип прямо в структуру (а ля класс) и прикручивай функцию-конструктор/фабрику, которая возвращает структуру с заполненными указателями на методы. Вот тогда будет как бы полиморфизм.

no-such-file ★★★★★
()

Но как это делают программисты в программировании?

Используют языки, подходящие под задачу.

eagleivg ★★★★★
()
Ответ на: комментарий от AlexVR

Внешне, должно быть +/- так:

Не должно быть.

animal_new, cat_new, dog_new, animal_free, cat_free, dog_free

Замечательно, только динамических аллокаций на каждый объект не хватало.

У ТС простой реквест: должно быть поле одного из двух типов. union из ссылок а) ничем не отличается от union любых других данных, б) полностью удовлетворяет реквест ТС. Никакого ООП-мусора ТС не просил.

Siborgium ★★★★★
()
Ответ на: комментарий от eagleivg

А вы, наверное, знаете ТС лично и готовы нам рассказать, какую задачу он решает и почему С для нее не подходит?

Siborgium ★★★★★
()
Ответ на: комментарий от no-such-file

Вот кстати, да...

Соглашусь.

И какое это имеет отношение к полиморфизму? Хочешь играть в ООП, пихай указатели на функции обрабатывающие тип прямо в структуру (а ля класс) и прикручивай функцию-конструктор/фабрику, которая возвращает структуру с заполненными указателями на методы. Вот тогда будет как бы полиморфизм.

Приведённые решения ничего не имеют общего с полиморфизмом. Для простоты (чисто для простоты и не более, т.к. механизм несколько иной и более сложный) можно представить С++ класс как структуру, в которой есть неявный (невидимый) элемент – vtable. Именно благодаря vtable и достигается полиморфизм, т.е., способность объекта использовать методы производного класса, который не существует на момент создания базового. См. здесь.

Moisha_Liberman ★★
()
Ответ на: Вот кстати, да... от Moisha_Liberman

можно представить С++ класс

Только не нужно. Никто про С++ не просил.

механизм несколько иной и более сложный

Но это не важно, по той же причине.

firkax ★★★★★
()
Ответ на: комментарий от firkax

А в чём разница?

Хотите полиморфизм в С? Ну так будьте любезны явно описать (создать) vtable и использовать её. Врукопашную, т.к. компиль Вам здесь не в помощь, он ничего об этом не знает. В противном случае это не столько полиморфизм, сколько его попытка. Боюсь только что несколько нерезультативная. Хотя, до определённых пределов, возможно что и сработает. Типа, разработать аналог полиморфизма в С++. Смысл только непонятен.

Moisha_Liberman ★★
()
Ответ на: комментарий от Siborgium

У ТС простой реквест

ТС явно не договаривает (или не может объяснить). Если надо хранить ссылки на разные объекты в одном месте, то это явный признак остановиться и подумать, что пошло не так.

А если это ещё называют полиморфизмом, то возводим уровень опасности в квадрат.

Никакого ООП-мусора ТС не просил.

Название темы «Полиморфизм в Си».

animal_new, cat_new, dog_new, animal_free, cat_free, dog_free

Замечательно, только динамических аллокаций на каждый объект не хватало.

Ну это только детали. Ну добавим X_init and X_destroy, или будем структуры инициализировать вручную как глобальные static, или ещё как. Это всё только детали. Полиморфизм в Си надо начинать с:

struct animal {
  // ...
};

struct cat {
  struct animal base;
  // ...
};

struct dog {
  struct animal base;
  // ...
};

Тогда за счёт первого члена преобразования struct cat * -> struct animal * корректны. А проверку обратного преобразования не сложно организовать (через тот же vtable).

union же стоит использовать либо для организации Variant (и правильная реализация есть выше), либо для преобразования несвязанных типов.

НО

ТС молчит, а мы филосовствуем.

AlexVR ★★★★★
()
Ответ на: А в чём разница? от Moisha_Liberman

vtable не обязательно должно быть в виде указателя на некую таблицу в описываемой структуре. И оно не обязательно вообще выделимо в качестве отдельной сущности. C++ это только один из вариантов реализации.

firkax ★★★★★
()
Ответ на: Да. Вы правы. от Moisha_Liberman

Из того что C++ сделан на базе C, вообще никак не следует, что все реализации полиморфизма на базе С должны быть как С++. Даже наоборот: хочешь С++ реализацию - бери С++ и не изобретай его клон (тем более что в С++ даже поддержка на уровне спец. синтаксиса к этому всему сделана, которой у клона не будет). Хочешь другую - бери С и дорабатывай к нему.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)
enum StructKind {
	charKind,
	intKind,
};

struct CharStruct {
	char val;
};

struct IntStruct {
	char val;
};

struct Struct {
	enum StructKind kind;
	union {
		struct CharStruct chSt;
		struct IntStruct intSt;
	};
};

X512 ★★★★★
()

или на или же на

это не полиморфизм, это сумма типов. в С делается при помощи ущербного (как и сам язык в общем-то) union.

anonymous
()
Ответ на: комментарий от yufhgigibi

Потому что под этим термином принято понимать или параметрический, или ad-hoc полиморфизм. А здесь просто сумма типов.

Но так как в Си нет родной поддержки суммы типов, приходится изобретать велосипед.

Структуры могут занимать совершенно разный объём памяти, неприятно их держать в одних рамках.

Держите указатели в union, по смыслу разницы нет.

Всё равно придётся держать отдельное поле тэга и проверять тип указателя по нему. Будь это Паскаль, это делал бы сам компилятор.

wandrien ★★
()
Ответ на: комментарий от AlexVR

Именно так, мне нужно держать разные структуры в одном массиве.

Это неправильная задача?

Структура в структуре мне не поможет. Я не вижу тут «много форм».

Как написать функцию одновременно принимающую struct cat и struct dog, но не принимающую struct plane? Не знаю, даже void* не совсем то.

Объединения мне очень понравились.

yufhgigibi
() автор топика
Ответ на: комментарий от wandrien

Спасибо за ответ.

Держите указатели в union…

Так и делаю, спасибо.

yufhgigibi
() автор топика
Ответ на: комментарий от yufhgigibi

Как написать функцию одновременно принимающую struct cat и struct dog, но не принимающую struct plane?

Что-то типа такого:


struct cat {

};

struct dog {

};


enum animal_type {
    type_cat,
    type_dog
};

typedef struct {
    enum animal_type type;
    union {
        struct cat * cat;
        struct dog * dog;
    };
} animal_pointer;

void f(animal_pointer p) {
    switch (p.type) {
        case type_cat:
            /* do something with p.cat */
            break;
        case type_dog:
            /* do something with p.dog */
            break;
        default:
            die("Invalid type tag in foo()!");
    }
}

wandrien ★★
()
Последнее исправление: wandrien (всего исправлений: 1)
Ответ на: комментарий от wandrien

Да, точно, спасибо. Не дочитал программу. Да, я так и делаю.

yufhgigibi
() автор топика
Ответ на: комментарий от wandrien

Всё равно придётся держать отдельное поле тэга и проверять тип указателя по нему.

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

Siborgium ★★★★★
()
Ответ на: комментарий от AlexVR

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

Нет, все пошло так, и это нормальный юзкейс.

Название темы «Полиморфизм в Си».

Без разницы, что написано в названии темы. Реквест четко обрисован в теле поста. Более того, с помощью этого приема полиморфизм как раз таки может быть организован.

union же стоит использовать

Там, где его стоит использовать. Вы готовы аргументировать проповедуемые вами ограничения?

Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

Или хранить в первом слове целевой структуры.

Много вариантов.

wandrien ★★
()
Последнее исправление: wandrien (всего исправлений: 1)
Ответ на: комментарий от anonymous

Сумма типов - это тоже полиморфизм, можно по разному достичь полиморфизма.

Int64 ★★★
()
Ответ на: комментарий от yufhgigibi

Пусть за меня пишет компилятор, я сам не люблю.

Тогда надо брать C++. Си — это тонкая надстройка над ассемблером.

X512 ★★★★★
()
Ответ на: комментарий от wandrien

Так будет больше объектов кучи и больше фрагментация.

X512 ★★★★★
()
Ответ на: комментарий от yufhgigibi

Структуры могут занимать совершенно разный объём памяти

Можно при выделении памяти указать размер нужной структуры.

struct Struct *NewStruct(enum StructKind kind)
{
  size_t size;
  struct Struct *res;
  switch (kind) {
    case charKind: size = sizeof(CharStruct); break;
    case intKind:  size = sizeof(IntStruct);  break;
    default: return NULL;
  }
  res = malloc(sizeof(enum StructKind) + size);
  res->kind = kind;
  return res;
}
X512 ★★★★★
()
Последнее исправление: X512 (всего исправлений: 1)
Ответ на: комментарий от firkax

C однозначно подходит под большинство задач. Если его пользователь компетентен.

У Вас ус отклеился слова однозначно и если в одном посте.

eagleivg ★★★★★
()
Ответ на: комментарий от Siborgium

А вы, наверное, знаете ТС лично

Зачем мне это?

готовы нам рассказать, какую задачу он решает и почему С для нее не подходит?

Конечно. Потому что запрошен полиморфизм, которого в C нету. Как и классов. Я уж молчу, что такого рода «полиморфизм» из юнионов структур потом стреляет в ногу при переходе от BE к LE и обратно, а также при передаче потом этих структур по сети.

Да и вообще, если в используемом языке задача решается костылями - значит язык для решения задачи выбран неверно.

eagleivg ★★★★★
()
Ответ на: комментарий от eagleivg

«Однозначно» относится к языку и задачам, «если» - к программисту. Некомпетентных надо учить или выгонять, а не языки им подбирать.

firkax ★★★★★
()
Ответ на: комментарий от firkax

«Однозначно» относится к языку и задачам, «если» - к программисту.

Точно, ведь язык, задачи и программист никак же не связаны!

Но вернемся чуть назад. Если «C однозначно подходит под большинство задач», почему же он остался только в ведре да в эмбедде? Бровзеры да игрульки - на плюсах чой-то пишут. Вебня - js на фронте и зоопарк на бэке (но не си). Ынтерпрайз - жаба да шарп. Может наука? - нет, и тут фортран. Прототипирование - пыхтон. Никого не забыл? Так почему большинство задач пишутся на чем угодно, но не на сях?

eagleivg ★★★★★
()
Ответ на: комментарий от eagleivg

Че-то эта простыня дает сколько угодно информации о твоем кругозоре, но ничего полезного о C.

t184256 ★★★★★
()
Ответ на: комментарий от eagleivg

полиморфизм, которого в C нету.

Да ладно?

Как и классов.

Которые для реализации полиморфизма нужно потому, что …?

стреляет в ногу при переходе от BE к LE

при передаче потом этих структур по сети.

Вас кто-то спрашивал про передачу union’ов по сети и про переход от BE к LE?

если в используемом языке задача решается костылями

Это не костыли, а каноничный способ реализации, используемый в множестве проектов на С.

Siborgium ★★★★★
()
Ответ на: комментарий от eagleivg

Может наука? - нет, и тут фортран.

Да ладно? (x2)

Бровзеры да игрульки - на плюсах чой-то пишут.

Это не плюсы, а С с классами.

Так почему большинство задач пишутся на чем угодно, но не на сях?

Еще раз, никому нет дела до того, на чем пишется большинство задач. У ТС есть его задача, которую он решает. Эта задача неизвестна -> сделать вывод о пригодности или непригодности С невозможно.

Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

Да ладно?

Прикинь?

Которые для реализации полиморфизма нужно потому, что …?

Может, почитаешь определение полиморфизма?

Это не костыли, а каноничный способ реализации, используемый в множестве проектов на С.

О, а за тонкий юмор зачет:)

eagleivg ★★★★★
()
Ответ на: комментарий от Siborgium

Да ладно? (x2)

Прикинь? (x2)

Это не плюсы, а С с классами.

Давно уже нет

Эта задача неизвестна -> сделать вывод о пригодности или непригодности С невозможно.

ТС решает задачу, языковыми средствами не предусмотренную. Вывод?

eagleivg ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.