Я пытаюсь сделать небольшую библиотеку для управления частицами, которая позволяет «расширять» структуру данными пользователя (текстуры, кадры анимации и т. Д.). Библиотека будет знать только размер расширенной структуры. Как выполнить итерацию по массиву неизвестных типов структур, но известного размера структуры?

typedef struct{
    int type;
}Base;

typedef struct{
    Base base;
    int value;
}inherited;

int main(){
    size_t size = sizeof(inherited);
    int count = 10;
    
    void *b = malloc(size * count);
    
    for (int i = 0; i < count; i++){
        // iterate based on known size & send to callback
        callback( &( (size)b )[i] );
    }
    
    free(b);
    return 0;
}
0
NeZvers 24 Сен 2021 в 14:57

4 ответа

Лучший ответ
char *b = malloc(size * count);

for (int i = 0; i < count; i++){
    // iterate based on known size & send to callback
    callback( b + i * size );
}
1
0___________ 24 Сен 2021 в 13:19

Я предполагаю, что код, который выполняет malloc и вызывает callback, ничего не знает о типе объекта, только о его размере.

#include <stdlib.h>

void *alloc_and_init(size_t nmemb, size_t size, void (*callback)(void *))
{
    void *b = calloc(nmemb, size);
    if (b)
    {
        char *p = b;
        for (size_t i = 0; i < nmemb; i++)
        {
            callback(p);
            p += size;
        }
    }
    return b;
}

typedef struct{
    int type;
}Base;

typedef struct{
    Base base;
    int value;
}inherited;

void init_inherited(void *p)
{
    inherited *obj = p;
    /* do initialization of obj->* here */
}

int main(void)
{
    int objcount = 10;
    inherited *objarr;

    objarr = alloc_and_init(objcount, sizeof(*objarr),
                            init_inherited);
    /* ... */
    free(objarr);
}

2
Ian Abbott 24 Сен 2021 в 12:34
for( inherited *p = b, *e = p + count; p < e; p++ ){
    callback(p);
}
1
William Pursell 24 Сен 2021 в 12:18

Полиморфизм в C всегда довольно неуклюжий. В основном вам нужно создать "vtable" вручную. Наивная упрощенная версия ниже позволяет каждому объекту иметь свой собственный указатель на функцию. В итоге вы получите что-то довольно надуманное:

#include <stdio.h>
#include <stdlib.h>

typedef struct base_t base_t;
typedef void callback_t (const base_t* arg);

struct base_t
{
  int type;
  callback_t* callback;
};

typedef struct
{
  base_t base;
  int value;
} inherited_t;


void callback_base (const base_t* arg)
{
  puts(__func__);
}

void callback_inherited (const base_t* arg)
{
  const inherited_t* iarg = (const inherited_t*)arg;
  printf("%s value: %d\n", __func__, iarg->value);
  
}

int main (void)
{
  // allocate memory
  base_t* array [3] = 
  {
    [0] = malloc(sizeof(inherited_t)),
    [1] = malloc(sizeof(base_t)),
    [2] = malloc(sizeof(inherited_t)),
  };

  // initialize objects
  *(inherited_t*)array[0] = (inherited_t){ .base.callback=callback_inherited, .value = 123 };
  *(array[1]) = (base_t){ .callback=callback_base };
  *(inherited_t*)array[2] = (inherited_t){ .base.callback=callback_inherited, .value = 456 };
  
  for (int i = 0; i < 3; i++)
  {
    array[i]->callback(array[i]); // now we get polymorphism here
  }
}

Более профессиональная версия предполагает написание единицы перевода (.h + .c) для каждого «класса», а затем объединение выделения с инициализацией в «конструкторе». Он будет реализован с использованием непрозрачного типа, см. Как сделать частную инкапсуляцию в C? Внутри конструктора , установите vtable, соответствующую типу выделенного объекта.

Я также смело заявляю, что любое объектно-ориентированное решение, использующее аргументы void*, имеет некоторую конструктивную ошибку. Интерфейс должен использовать указатель базового класса. Указатели на пустоту опасны.

0
Lundin 24 Сен 2021 в 13:19