Способы запрещать полиморфные свойства в обычном С? - PullRequest
0 голосов
/ 10 ноября 2018

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

Пример этого:

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

typedef void (*update_t)(void *);

typedef struct entity entity_t;
typedef struct compA compA_t;
typedef struct compB compB_t;


struct compA{

    update_t update;
};

struct compB{

    update_t update;
};

struct entity{
    update_t update;
    int curSize;
    void **components;
};


void compA_update(void *c){
    printf("updating: componentA\n");
}

compA_t *compA_create(){
    compA_t *c = malloc(sizeof(compA_t));
    c->update = compA_update;
    return c; 
}

void compB_update(void *c){
    printf("updating: componentB\n");
}


compB_t *compB_create(){
    compB_t *c = malloc(sizeof(compB_t));
    c->update = compB_update;
    return c; 
}


void entity_update(void *en){
    entity_t *e = (entity_t *)en;
    for(int i = 0; i < e->curSize; i++){
        //would like to somehow update all the components with one line just iterating through the array but does not seem possible
    }
    return;
}



entity_t *entity_create(){
    entity_t *e = malloc(sizeof(entity_t));
    e->curSize = 0;
    e->update = entity_update;
    calloc(32, sizeof(void *));
    return e;
}

void add_component(entity_t *e, void *c){
    printf("%d\n", e->curSize);
    e->components[e->curSize] = c;
    e->curSize++;
    return;
}




int main(void){

    entity_t *e = entity_create();
    compA_t *a = compA_create();
    compB_t *b = compB_create();
    add_component(e, a);
    add_component(e, b);

    e->update(e);

    return 0;
}

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

Есть ли лучший способ сделать это? Поскольку подход с переключением будет довольно сумасшедшим довольно быстро, если в массиве будет много разных типов. это означает, что нужно явно добавлять наблюдения для каждого типа, и каждый случай делает одно и то же, что в этом случае вызывает указатель на функцию с именем «update».

1 Ответ

0 голосов
/ 10 ноября 2018

Вы можете попробовать полиморфизм данных вместо указателя функции. То есть разные данные приводят к разному поведению, используя один и тот же код.

Например, простое полиморфное поведение:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <assert.h>

typedef const char* ccstr;

typedef struct animal_attr_t
{
    bool is_body_segmented;
    float gill_completeness;
    float lung_completeness;
} animal_attr_t;

typedef struct species
{
    ccstr name, kingdom, domain;

    animal_attr_t animal_attr[0];
} species;

void initialize_species_base(species *this, ccstr name, ccstr kingdom, ccstr domain)
{
    this->name = name;
    this->kingdom = kingdom;
    this->domain = domain;
}

void initialize_animal_attr(animal_attr_t *this, bool is_body_segmented, float gill_completenss, float lung_completeness)
{
    this->is_body_segmented = is_body_segmented;
    this->gill_completeness = gill_completenss;
    this->lung_completeness = lung_completeness;
}

void print_species(species*);

int main(int argc, char *argv[])
{
    species *yeast = calloc(sizeof(species), 1);
    assert(yeast);

    initialize_species_base(yeast, "yeast", "fungus", "eukaryote");

    print_species(yeast);

    species *dog = calloc(sizeof(species) + sizeof(animal_attr_t), 1);
    assert(dog);

    initialize_species_base(dog, "dog", "animal", "eukaryote");
    initialize_animal_attr(dog->animal_attr, true, 0.0f, 1.0f);

    print_species(dog);

    free(yeast);
    free(dog);
}

void print_species(species *this)
{
    printf("name = %s, kingdom = %s, domain = %s",
           this->name, this->kingdom, this->domain);

    if (strcmp(this->kingdom, "animal") == 0) {
        animal_attr_t *ani_attr = this->animal_attr;
        printf(", has %s, %f completeness of gill, %f completeness of lung",
               ani_attr->is_body_segmented ? "segmented body" : "unsegmented body",
               ani_attr->gill_completeness, ani_attr->lung_completeness);
    }

    printf(".\n");
}

yeast и dog - это 2 совершенно разных типа, но с species это выражается унифицированным образом, а print_species имеет полиморфное поведение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...