NeZvers
NeZvers

Reputation: 120

C iterate array by known sizeof

I'm trying to make a small library for particle management that allows to "expand" struct with user's data (texture, animation frames, etc). The library would know only the size of the expanded struct. How do I iterate through the array of unknown struct types but known size of a struct?

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;
}

Upvotes: 0

Views: 118

Answers (4)

0___________
0___________

Reputation: 67979

char *b = malloc(size * count);

for (int i = 0; i < count; i++){
    // iterate based on known size & send to callback
    callback( b + i * size );
}

Upvotes: 0

Lundin
Lundin

Reputation: 214810

Polymorphism in C is always rather clunky. Basically you have to construct a "vtable" manually. The naive, simplified version below lets each object have its own function pointer. You'll end up with something rather contrived like this:

#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
  }
}

A more professional version involves writing a translation unit (.h + .c) per "class" and then combine allocation with initialization in the "constructor". It would be implemented with opaque type, see How to do private encapsulation in C? Inside the constructor, set the vtable corresponding to the type of object allocated.

I'd also boldly claim that any OO solution using void* arguments has some design flaw. The interface should be using the base class pointer. Void pointers are dangerous.

Upvotes: 0

Ian Abbott
Ian Abbott

Reputation: 17513

I assume the code that does the malloc and calls callback doesn't know anything about the type of the object, only its size.

#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);
}

Upvotes: 2

William Pursell
William Pursell

Reputation: 212564

for( inherited *p = b, *e = p + count; p < e; p++ ){
    callback(p);
}

Upvotes: 1

Related Questions