简体   繁体   中英

Best way to store different structs in same array in c

If I have multiple structs which have a common method like this:

typedef struct s sphere;
typedef struct s{
    point3 center;
    double radius;
    bool (*hit)(sphere, const ray*, double, double, hit_record*);

} sphere;

typedef struct b box;
typedef struct b{
    point3 center;
    double radius;
    bool (*hit)(box, const ray*, double, double, hit_record*);

} box;

Is it possible for me to create some type of array such that it can store both of these structs and loop through it calling the method like this:

objects shapes[50]; // Where objects is something that can let shapes hold both structs


//Pretend shapes has a mixture of Boxes and Spheres
int number_of_shapes = 20;

for (int i = 0; i < number_of_shapes;i++){
    if shapes[i].hit(shapes[i], r, t, t, red) { //random variables
        ;// do something
    }
} 

I just started learning c yesterday so I am not really sure what to do. I tried using void pointers but that also failed miserably.

Void pointers are the right way to do that, but it requires that your objects are persistent in memory, because an array of pointers won't retain them. This would be possible, for instance, if you allocate each object using malloc.

A good way to do that with a lot of objects, would be to use two helper functions: one to allocate an instance and add a pointer in the array, another to remove that pointer from the array and free the structure it points to. But this supposes managing a dynamic sized array, which is another question.

Another solution, maybe simpler, would be to have two arrays of structures (not pointers), one for each type, just to maintain the objects alive, and an array of void pointers (pointing to indices of these two arrays) to mix them.

Here is a rough cut of how you might achieve your objective...

enum { BOX, SPHERE } types;

typedef struct sphere {
    point3 center;
    double radius;
} sphere_t;

typedef struct box {
    point3 center;
    double len, wid, dpth;
    double rotX, rotY, rotZ;
} box_t;

typedef object {
    int type;
    union {
        sphere_t sphere;
        box_t box;
    };
} object_t;

int main() {
    object_t objs[ 50 ];
    /* ... */
    for( int i = 0; i < numObjs; i++ )
        switch( objs[ i ].type ) {
            case BOX:
                hndlr_Box( objs[i].box );
                break;
            case SPHERE:
                hndlr_Sphere( objs[i].sphere );
                break;
            /* ... */
        }
}

Looking back at that, it might be even better to use a linked list of objects instead of an array.

AND, each object instance in the array will consume as much memory as is required for the largest of the union. It may be better to use pointers to separately allocated blocks, each storing only its particular attributes.

If you create a sufficiently complicated enough object, you should be able to accomplish something pretty close to your goal. Below, I sketch out a solution that you should be able to build upon.

First, we define a Shape as having a ShapeInterface , which for now is just something that can be hit .

typedef struct HitRecord HitRecord;
typedef struct Ray Ray;

typedef struct Shape {
    const struct ShapeInterface * const vtable;
} Shape;

struct ShapeInterface {
    bool (*hit)(Shape *, const Ray *, double, double, HitRecord *);
    void (*dump)(Shape *);
};

bool shape_hit (Shape *s, const Ray *r, double x, double y, HitRecord *hr) {
    return s->vtable->hit(s, r, x, y, hr);
}

void shape_dump (Shape *s) {
    s->vtable->dump(s);
}

Then, we define Sphere and Box to be kinds of Shape .

typedef struct Point3 {
    double p[3];
} Point3;

typedef struct Sphere {
    Shape base;
    Point3 center;
    double radius;
    bool popped;
} Sphere;

typedef struct Box {
    Shape base;
    Point3 center;
    double radius;
    bool crushed;
} Box;

Now, define Objects to be a union of these different kinds of Shape . We note that all items in Objects have a common initial sequence, which is Shape .

typedef union Objects {
    Shape shape;
    Sphere sphere;
    Box box;
} Objects;

When you have an array of Objects , you can loop through and call shape_hit on the shape member.

void process_hit_objects (
        Objects shapes[],
        int number_of_shapes,
        const Ray *r, double x, double y, HitRecord *hr) {

    for (int i = 0; i < number_of_shapes; ++i) {
        if (shape_hit(&shapes[i].shape, r, x, y, hr)) {
            /* ... do something ... */
            shape_dump(&shapes[i].shape);
        }
    }
}

When creating a Sphere , it needs to initialize its base member with an appropriate implementations for the functions in its vtable .

static bool sphere_hit (
        Shape *shape, const Ray *r, double x, double y, HitRecord *hr) {
    Sphere *sphere = (void *)shape;
    return sphere->popped;
}

static void sphere_dump (Shape *shape) {
    Sphere *sphere = (void *)shape;
    double *p = sphere->center.p;
    printf("sphere: %p @ <%f,%f,%f> |%f|\n",
           (void *)sphere, p[0], p[1], p[2], sphere->radius);
}

void makeSphere (Sphere *s, Point3 center, double radius) {
    static const struct ShapeInterface vtable = {
        sphere_hit, sphere_dump
    };
    Shape base = { &vtable };
    Sphere sphere = { base, center, radius, true };
    memcpy(s, &sphere, sizeof(sphere));
}

Similarly for a Box .

static bool box_hit (
        Shape *shape, const Ray *r, double x, double y, HitRecord *hr) {
    Box *box = (void *)shape;
    return box->crushed;
}

static void box_dump (Shape *shape) {
    Box *box = (void *)shape;
    double *p = box->center.p;
    printf("box: %p @ <%f,%f,%f> |%f|\n",
           (void *)box, p[0], p[1], p[2], box->radius);
}

void makeBox (Box *b, Point3 center, double radius) {
    static const struct ShapeInterface vtable = {
        box_hit, box_dump
    };
    Shape base = { &vtable };
    Box box = { base, center, radius, true };
    memcpy(b, &box, sizeof(box));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM