I was working on a way to create dynamic arrays in C, and I came up with this solution as a general structure for how I want my functions/macros to work:
//dynarray.h
#define dynarray(TYPE)\
struct{\
TYPE *data;\
size_t size;\
size_t capacity;\
}
int dynarray_init_internal(void **ptr, size_t *size, size_t *cap, size_t type_size, size_t count);
#define dynarray_init(ARR, SIZE) dynarray_init_internal(&ARR->data, &ARR->size, &ARR->capacity, sizeof(*ARR->data), SIZE)
//dynarray.c
int dynarray_init_internal(void **ptr, size_t *size, size_t *cap, size_t type_size, size_t count){
*ptr = malloc(type_size*count);
if(*ptr == NULL){
return 1;
}
*size = 0;
*cap = count;
return 1;
}
Is this an acceptable approach to have a generic function/macro combo that deals with dynamically allocating memory in a type agnostic way?
The only doubts I have about this is that I'm not sure if this is undefined behavior or not. I imagine this could be easily expanded for other functions that are typically expected for a dynamic array structure. The only issue I can see with it is that since it's an anonymous struct you can't pass it as an argument anywhere (easily at least), but that can be easily fixed by creating a dynarray_def(TYPE, NAME)
macro which would define a dynamic array struct with NAME
and have it hold data of TYPE
while still having it work with all the other function/macro style listed above.
This is undefined behavior because you're converting (for example) an int **
to a void **
and dereferencing it to yield a void *
. The automatic conversion to/from a void *
does not extend to void **
. Reading/writing one type as another (in this case, writing a int *
as a void *
) is in violation.
The best way to handle this is to make the entire init routine a macro:
#define dynarray_init(ARR, SIZE) \
do {\
(ARR)->data = malloc(sizeof(*(ARR)->data*(SIZE));\
if ((ARR)->data == NULL){\
_exit(1);\
}\
(ARR)->size = 0;\
(ARR)->capacity = (SIZE);\
} while (0)
EDIT:
If you're looking to shy away from function-like macros, you can instead use a macro to create a function and the struct type it works with:
#include <stdio.h>
#include <stdlib.h>
#define dynarray(TYPE)\
struct dynarray_##TYPE {\
TYPE *data;\
size_t size;\
size_t capacity;\
};\
\
int dynarray_##TYPE##_init(struct dynarray_##TYPE **ptr, size_t count){\
*ptr = malloc(sizeof(*ptr)*count);\
if(*ptr == NULL){\
return 1;\
}\
\
(*ptr)->size = 0;\
(*ptr)->capacity = count;\
return 1;\
}
// generate types and functions
dynarray(int)
dynarray(double)
int main()
{
struct dynarray_int *da1;
dynarray_int_init(&da1, 5);
// use da1
struct dynarray_double *da2;
dynarray_double_init(&da2, 5);
// use da2
return 0;
}