This project demonstrates how to bring generic programming into the C language by using Mustache, a lightweight logic-less template engine.
C lacks native support for generics. Instead of relying on macros or duplicating similar code for each data type, this approach uses Mustache templates to generate reusable, type-safe stack implementations in plain C.
The repository includes:
- Mustache templates for
.c
and.h
files of a stack structure. - A simple code generation pipeline using
make
. - Two test cases demonstrating usage with
int
and a custom wrapper type.
This project shows that even in a low-level language like C, meta-programming techniques can improve maintainability and reduce boilerplate — all without giving up performance or transparency.
C macros and conditional compilation can simulate some forms of generic code, but they often come with trade-offs:
- 🧹 Complex syntax – Macro logic can quickly become unreadable and hard to debug.
- 🚫 Limited flexibility – Features like conditional defaults or type-dependent expansion are hard to express.
- 🧪 Poor tooling support – IDEs and linters usually struggle with macro-heavy code.
Using a template engine like Mustache provides a different approach:
- ✅ Clear separation between code and logic – Templates stay readable and simple.
- ✅ Flexible output – You can generate
.c
/.h
files for any type combination with consistent formatting. - ✅ Static output – The generated code is plain C, fully transparent to compilers and debuggers.
- ✅ Easy integration – Combine with
make
or shell scripts for reproducible, automated generation.
This method doesn’t replace C’s core mechanisms — it augments them through pre-processing. It’s especially useful when you want to maintain clean, consistent, reusable data structures across multiple types without relying on compiler extensions.
The following test demonstrates how to use a generated stack for a user-defined type int_obj_t
. The type wraps an int
value, and provides custom clone
and free
functions for memory safety.
// int_obj_t is a wrapper around int with malloc/free.
typedef struct {
int data;
} int_obj_t;
Here's a minimal usage scenario:
#include "stack_obj.h"
int_obj_t * obj_new(int data);
void * obj_clone(void *obj);
void obj_free(void *obj);
int main(void)
{
stack_t *s = stack_new((stack_params_t){
.clone = obj_clone,
.free = obj_free
});
int data[] = {3, 4, 5, 6};
for (size_t i = 0; i < 4; i++) {
stack_push(s, obj_new(data[i]));
int_obj_t *top = stack_peek(s);
assert(top->data == data[i]);
obj_free(top);
}
// Pops: 6, 5, 4, 3
for (int i = 3; i >= 0; i--) {
int_obj_t *top = stack_pop(s);
assert(top->data == data[i]);
obj_free(top);
}
stack_free(s);
return 0;
}
make # Generates C code via Mustache and builds the test
./test_obj # Runs the test program
This confirms that the generated stack works with non-primitive types and handles memory ownership safely via callbacks.
- A modern C compiler
- GNU Make
mustach
(template processor)
Apache License 2.0 Copyright (c) 2019 ByteBard