0

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;
}
12
  • "How do I iterate through the array of unknown struct types but known size of a struct?" - if you don't know the type then you don't even know the size, do ya? Either you know both or you know none of the two. Commented Sep 24, 2021 at 12:01
  • 3
    Why don't your use a pointer to inherited? inherited *p = malloc(size * count); With this pointer you address each array item with callback(&p[i]); . Commented Sep 24, 2021 at 12:02
  • 1
    @SPlatten Your advice will work only after changing the b declaration from void* to char*. The void type has no size, so void* can't be used in pointer arithmetics. Commented Sep 24, 2021 at 12:23
  • 1
    Related: maybe typedef struct{ int type; size_t size; }Base; isn't such a bad idea? Commented Sep 24, 2021 at 12:47
  • 1
    @NeZvers What I mean is something like the answer from Ian Abbott. Commented Sep 24, 2021 at 12:56

4 Answers 4

2

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

Sign up to request clarification or add additional context in comments.

1 Comment

Yes, the perfect answer. I needed the only solution for the interaction but the solution is loud and clear. Thank you!
1
for( inherited *p = b, *e = p + count; p < e; p++ ){
    callback(p);
}

1 Comment

The inherited type is unknown, only base struct and sizeof(Inherited). Ian gave the perfect solution to cast into char and iterate by (i * size).
0
char *b = malloc(size * count);

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

3 Comments

Thank you! This is the most correct and compact answer I needed.
It would work better if instead of using a char* and then size, you create a structure that is the same size as 'size' and then you can use that.
@SPlatten no it will not.
0

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.

1 Comment

Thanks for your version, but it is too overkill. Ian Abbott posted the perfect solution. I just need to have a char pointer and iterate with (i * size).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.