I spent some time implementing a generic stack in C using macros. Apart from general bugs and bad practices in my code, I was wondering about the viability of an implementation like this that uses macros. Note that the use of assert is only for this code; if I were coding an actual implementation that would be served to users, I would find a better solution.
stack.h
#ifndef STACK_H
#define STACK_H
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#define stackdef(T, pfx) \
typedef struct pfx##stack { \
size_t capacity, N; \
T *a; \
} pfx##stack; \
\
/* returns a pointer to a stack */ \
pfx##stack *s##pfx##_create(void) { \
pfx##stack *st = malloc(sizeof *st); \
\
assert(st); \
st->capacity = 1; \
st->N = 0; \
assert((st->a = malloc(sizeof *st->a))); \
return st; \
} \
\
/* frees all memory that was allocated for the stack */ \
void s##pfx##_free(pfx##stack *st) { \
assert(st); \
free(st->a); \
free(st); \
} \
\
/* returns true if the stack is empty */ \
bool s##pfx##_isempty(const pfx##stack *st) { \
assert(st); \
return st->N == 0; \
} \
\
/* adds ITEM to the top of the stack */ \
void s##pfx##_push(pfx##stack *st, T item) { \
assert(st); \
if (st->N == st->capacity) { \
assert((st->a = realloc(st->a, (st->capacity *= 2) * sizeof st->a))); \
} \
st->a[st->N++] = item; \
} \
\
/* removes and returns the top element of the stack */ \
T s##pfx##_pop(pfx##stack *st) { \
assert(st); \
assert(st->N > 0); \
if (st->N == st->capacity / 4) { \
assert((st->a = realloc(st->a, (st->capacity /= 2) * sizeof st->a))); \
} \
return st->a[--(st->N)]; \
} \
\
/* iterates through the stack and performs FN on each element */ \
void s##pfx##_iterate(pfx##stack *st, void (*fn)(void *)) { \
for (int i = st->N - 1; i >= 0; --i) { \
fn(&st->a[i]); \
} \
}
#endif
Example client
#include "stack.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
stackdef(char *, str);
int getword(char **s);
int main(void) {
strstack *q = sstr_create();
char *s;
while (getword(&s)) {
if (s[0] == '-' && s[1] == '\0') {
printf("%s\n", (s = sstr_pop(q)));
free(s);
} else {
sstr_push(q, s);
}
}
return 0;
}
// read the next word from stdin into S and return its length
int getword(char **s) {
int c;
int len = 0;
assert((*s = malloc(sizeof **s)));
**s = '\0';
while ((c = getchar()) == ' ' || c == '\t' || c == '\n') {}
while (c != ' ' && c != '\t' && c != '\n' && c != EOF) {
(*s)[len++] = c;
assert((*s = realloc(*s, (len + 1) * sizeof **s)));
(*s)[len] = '\0';
c = getchar();
}
ungetc(c, stdin);
return len;
}