The dynamic memory allocation of the array's fields itself does not mimic std::vector, it's an extra indirection that C++ does not pay for. You can make it a non-opaque struct in C and copy it around.
Array array_create(size_t size, size_t sizeof_data) {
Array result;
result.size = size;
result.capacity = size;
if(size) {
result.data = malloc(size * sizeof_data);
} else {
result.data = NULL;
}
return result;
}In ARRAY_PUSH_BACK, it just inserts the element directly into the buffer, provided there is enough capacity. There is no separate allocation for an element.
You don't need to redefine the struct for each element type, since the macro casts 'data' (type void *) to whatever the array's type is.