2

I'm writing a dynamic array in C:

struct array {
  int len;
  int cap;
  void *data;
};

It's easy to store values if I know the type:

void set(array* a, int idx, int val) {
    ((int*)a->data)[idx] = val;
}

But I want to accept any type (void*):

void set(array* a, int idx, void* val) {
    a->data[idx] = val;
}

This obviously doesn't compile because 'void' is not assignable

As I understand, compiler needs information about the type to get its size and calculate the offset to access data.

Is there a way to do this on my own? Operate directly on bytes, something like

void set(array* a, int idx, void* val, int size) {
    *((char*)a->data + size * idx) = val;
}

Basically I want to make them similar to Go slices. You can read about their built in generic types here:

https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics

No templates, no unboxing. They use void* for data storage.

Alex
  • 34,581
  • 26
  • 91
  • 135
  • Do you want an "array" of uniform types or can every element be of a different type? – Swordfish Oct 20 '18 at 18:06
  • See my answer here: https://stackoverflow.com/a/52788499/5382650 This provides a baseline and several variants, one of which may be to your liking. – Craig Estey Oct 20 '18 at 18:07
  • @Swordfish, every element has the same type. I want to do it like in Go. Dynamic arrays without templates and unboxing. – Alex Oct 20 '18 at 18:15
  • Here's the second part: https://stackoverflow.com/a/52749660/5382650 You can use the various methods (e.g. `switch`, virtual function table, etc.) – Craig Estey Oct 20 '18 at 18:16

2 Answers2

2

I suppose a memcpy should do what you are looking for:

void set(array* a, int idx, void* val, int size) {
    memcpy(a->data + size * idx, val, size);
}
HAL9000
  • 2,138
  • 1
  • 9
  • 20
  • Thanks! I should have really thought of it. The source of the function was also useful to understand how pointers work in C: https://raw.githubusercontent.com/gcc-mirror/gcc/master/libgcc/memcpy.c – Alex Oct 20 '18 at 20:54
0

first of all you need to use void* * data - to store an array, and not a single pointer to an element.

from what you have [a->data = (void*)val; ] should run fine.

it's only a pointer to the data, and it can be stored as void. you will need to know how to read the data when you will want to de-reference it.

if val points to an int- in order to read it you will have to cast it to let the compiler know how many bytes to read. so : *(int *)(a->data);

H.cohen
  • 517
  • 3
  • 9
  • But then I'd have to do N mallocs for each void* in data, right? – Alex Oct 20 '18 at 18:30
  • if you get the length of the array: a->data = (void* *)calloc(_initSize,1); for example when you create the array – H.cohen Oct 20 '18 at 18:34
  • to add a new element: static void AddData( array* a, void* _item) { *( a->data + a->curr_cap) = _item; ++(a->curr_cap); } – H.cohen Oct 20 '18 at 18:39