I wrote a generic dynamic array using the preprocessor. My use case for it is a compiler I'm writing that uses dynamic arrays a lot, and my current void pointer implementation is becoming a bit hard to use as I have to remember the type each array stores when using it. I'm wondering if this is a good readable implementation and interface.
I'm aware that using an existing library would most likely be better, but as this is for learning purposes, I want to implement all the data structures I use.
#include <stdio.h> #include <stdlib.h> #include <assert.h> #define ARRAY_INITIAL_CAPACITY 8 #define Array(type) type * // [capacity][used][data]... #define initArray(array) do { \ size_t *data = malloc(2 * sizeof(size_t) + ARRAY_INITIAL_CAPACITY * sizeof(*(array))); \ data[0] = ARRAY_INITIAL_CAPACITY; \ data[1] = 0; \ (array) = (typeof(*(array)) *)&data[2]; \ } while(0) #define freeArray(array) free((size_t *)(array)-2) #define arrayCapacity(array) ((array) ? *((size_t *)(array)-2) : 0lu) #define arrayUsed(array) ((array) ? *((size_t *)(array)-1) : 0lu) #define arrayEmpty(array) (arrayUsed(array) == 0) #define arrayPush(array, value) do { \ if(arrayUsed(array) + 1lu >= arrayCapacity(array)) { \ size_t *data = ((size_t *)(array)-2); \ size_t newCap = arrayCapacity(array) * 2 + 2 * sizeof(size_t); \ data = realloc(data, newCap); \ (array) = (typeof(*(array)) *)&data[2]; \ } \ size_t *used = ((size_t *)(array) - 1); \ (array)[(*used)++] = (value); \ } while(0) #define arrayPop(array) (assert(arrayUsed(array) > 0), (array)[--*((size_t *)(array) - 1)]) #define arrayGet(array, idx) (assert((idx) < arrayUsed(array)), (array)[(idx)]) // callback type has to be void (*)(T, U) // where T is the type of the items in the array, // and U is the type of the user_data argument. #define arrayMap(array, callback, user_data) do { \ for(size_t i = 0; i < arrayUsed(array); ++i) {\ callback(array[i], (user_data)); \ } \ } while(0) #define arrayCopy(dest, src) do { \ for(size_t i = 0; i < arrayUsed(src); ++i) { \ arrayPush((dest), (src)[i]); \ } \ } while(0)
Example usage:
static void callback(int item, void *user_data) { printf("%d\n", item); } int main(void) { Array(int) nums = NULL; initArray(nums); arrayPush(nums, 1); arrayPush(nums, 2); printf("capacity: %lu, used: %lu\n", arrayCapacity(nums), arrayUsed(nums)); // Iterating for(int i = 0; i < arrayUsed(nums); ++i) { printf("%d\n", arrayGet(nums, i)); } // Iterating in reverse for(int i = arrayUsed(nums)-1; i >= 0; --i) { printf("%d\n", nums[i]); } Array(int) nums2 = NULL; initArray(nums2); arrayCopy(nums2, nums); while(!arrayEmpty(nums)) { printf("%d\n", arrayPop(nums)); } arrayMap(nums2, callback, NULL); freeArray(nums2); freeArray(nums); return 0; }