I made my own dynamic array type using void pointers. I'd like to know what you think of my implementation. void pointers are nice but I fear they may be inefficient. The compiler cannot tell what you are doing because it has no size information. Either way i like the flexibility and the fact that i do not have to use macros as much. But what are your thoughts on this? I am still working on it and am still finding some minor bugs here and there. It has been a fun project to work on.
main.c
#include "array.h"
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
void log_int(void *p);
int main(int argc, char const *argv[])
{
/* declare an integer array. */
int *a = array_alloc(sizeof(*a), log_int);
array_reserve(a, 32);
/*
append a list 1 - 9
fixed functions usually take in local arrays
or variadic macro arguments.
*/
array_give_fixed(a, 1, 2, 3, 4, 5, 6, 7, 8, 9);
/*
array_take usually stores the values taken
from an array into a buffer.
Passing NULL causes each elelemnt taken to have
its destructor called.
*/
array_take(a, NULL, 3);
int i[2];
/* array_take returns the number of elements taken. */
for(; array_take_fixed(a, i); )
{
fprintf(stdout, "%d ", i[0]);
fprintf(stdout, "%d\n", i[1]);
}
/*
array_free calls each elements destructor
and frees the array itself.
*/
array_free(a);
return 0;
}
void log_int(void *p)
{
int *i = p;
fprintf(stderr, "integer popped %d\n", *i);
}
array.h
#ifndef ARRAY_H
#define ARRAY_H
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#define COUNT(a) (sizeof(a) / sizeof *a)
typedef struct {
void (*freeElem)(void *);
size_t szElem;
size_t ctElem;
size_t cpElem;
} Array_Header;
void *array_alloc(size_t szElem, void (*freeElem)(void *));
void array_free_impl(void *a);
void *array_reserve_impl(void *a, size_t capacity);
void *array_push_impl(void *a, const void *elem);
void array_pop(void *a);
void *array_give_impl(void *a, const void *elems, size_t n);
size_t array_take(void *a, void *elems, size_t n);
size_t array_szElem(const void *a);
size_t array_ctElem(const void *a);
size_t array_cpElem(const void *a);
#define array_reserve(a, capacity) do { a = array_reserve_impl(a, capacity); } while(0)
#define array_push(a, elem) do { a = array_push_impl(a, elem); } while(0)
#define array_give(a, elems, n) do { a = array_give_impl(a, elems, n); } while(0)
#define array_give_fixed(p, ...) \
do { \
p = array_give_impl( \
p, &(__typeof__(*p)[]){__VA_ARGS__}, \
sizeof((__typeof__(*p)[]){__VA_ARGS__}) \
/ sizeof(__typeof__(*p))); \
} while(0)
#define array_take_fixed(p, a) \
array_take(p, a, sizeof(a) / sizeof(*a))
#define array_free(a) do { array_free_impl(a); a = NULL; } while(0)
#endif /* ARRAY_H */
array.c
#include "array.h"
void *array_alloc(size_t szElem, void (*freeElem)(void *))
{
void *a = malloc(sizeof(Array_Header));
Array_Header *header = a;
header->freeElem = freeElem;
header->szElem = szElem;
header->ctElem = 0;
header->cpElem = 0;
return header + 1;
}
void array_free_impl(void *a)
{
assert(a);
Array_Header *header = (Array_Header *)a - 1;
if(header->freeElem)
{
unsigned char *begin = a;
unsigned char *end = begin + header->ctElem * header->szElem;
for(; begin != end; begin += header->szElem)
{
header->freeElem(begin);
}
}
free(header);
}
void *array_reserve_impl(void *a, size_t capacity)
{
assert(a);
Array_Header *header = (Array_Header *)a - 1;
if(capacity > header->cpElem)
{
header->cpElem = capacity;
header = realloc(header, sizeof(*header) + header->cpElem * header->szElem);
a = header + 1;
assert(header);
}
return header + 1;
}
void *array_push_impl(void *a, const void *elem)
{
assert(a);
assert(elem);
Array_Header *header = (Array_Header *)a - 1;
if(header->ctElem + 1 > header->cpElem)
{
header->cpElem = (header->cpElem + 1) * 2;
header = realloc(header, sizeof(*header) + header->cpElem * header->szElem);
a = header + 1;
assert(header);
}
memcpy((unsigned char *)a + header->ctElem * header->szElem, elem, header->szElem);
++header->ctElem;
return header + 1;
}
void array_pop(void *a)
{
assert(a);
Array_Header *header = (Array_Header *)a - 1;
if(header->ctElem > 0)
{
--header->ctElem;
if(header->freeElem)
{
unsigned char *p = (unsigned char *)a + header->ctElem * header->szElem;
header->freeElem(p);
}
}
}
void *array_give_impl(void *a, const void *elems, size_t n)
{
assert(a);
assert(elems);
Array_Header *header = (Array_Header *)a - 1;
if(header->ctElem + n > header->cpElem)
{
header->cpElem = (header->cpElem + n) * 2;
header = realloc(header, sizeof *header + header->cpElem * header->szElem);
a = header + 1;
assert(header);
}
memcpy((unsigned char *)a + header->ctElem * header->szElem, elems, n * header->szElem);
header->ctElem += n;
return header + 1;
}
size_t array_take(void *a, void *elems, size_t n)
{
assert(a);
Array_Header *header = (Array_Header *)a - 1;
// if(header->ctElem >= n)
n = n > header->ctElem ? header->ctElem : n;
{
header->ctElem -= n;
if(elems)
{
memcpy(elems, (unsigned char *)a + header->ctElem * header->szElem, n * header->szElem);
}
else
{
unsigned char *begin = (unsigned char *)a + header->ctElem * header->szElem;
unsigned char *end = begin + n * header->szElem;
for(; begin != end; begin += header->szElem)
header->freeElem(begin);
}
}
return n;
}
size_t array_ctElem(const void *a)
{
assert(a);
Array_Header *header = (Array_Header *)a - 1;
return header->ctElem;
}
size_t array_cpElem(const void *a)
{
assert(a);
Array_Header *header = (Array_Header *)a - 1;
return header->cpElem;
}
size_t array_szElem(const void *a)
{
assert(a);
Array_Header *header = (Array_Header *)a - 1;
return header->szElem;
}
```