I am a big fan of C, but some part of me always yearn to have just
enough higher level constructs.
The impracticality of memory allocation in C is one of my pet peeves.
Being very easily distracted, I tend to forget to free my memory, or
close my resources, so you might guess that when I learned about
smart pointers in C++, I was immediately hooked.
As a freshman in my IT engineering school, our first semester was mostly
about programming in C. We were assigned pretty simple, solo projects,
until the big project came in — we had to implement a POSIX bourne
shell, as a team of 4 people, and we were warned that if we leaked,
we would eat a pretty hardcore malus on our final grade. We also had
to use gcc 4.9 as our compiler.
Because I knew that my teammates and I were certainly not perfect, I
decided to try and make our life easier. I first thought about
wrapping malloc to register all allocated data, and
then free everything before exiting, but it wasn’t a satisfying solution.
I went and read a little more about gcc’s attributes, and found
__attribute__ ((cleanup(f))
: according to the documentation,
this variable attribute would call the function f
on the variable
before exiting the current scope.
I was delighted.
Implementing an auto-cleaning variable
I went on and figured that I would simply make some kind of
would-be type attribute to make the variable free itself after
going out of scope:
#define autofree __attribute__((cleanup(free_stack)))
__attribute__ ((always_inline))
inline void free_stack(void *ptr) {
free(*(void **) ptr);
}
The usage was pretty straight forward:
1
2
3
4
5
int main(void) {
autofree int *i = malloc(sizeof (int));
*i = 1;
return *i;
}
But while it worked well for these tests, when I started refactoring
the existing code with the new keyword, it simply didn’t prove to be
that useful, because most of the dynamically allocated data was
complex data, with (also dynamically allocated) members, and since you
can’t slap a cleanup attribute on a struct member, I had to do better.
I needed some kind of destructor function mechanism.
I decided to prepend some metadata to the allocated memory – this ought
to be the most non-intrusive way to work my way to the new goal:
|------------------------|---------------------- // -----|
| metadata | padding? | actual data |
|------------------------|---------------------- // -----|
^ ^ returned address (word aligned)
`- start of allocated
block
I needed two functions: one to allocate the memory, and one to free it.
Hence came into existence smalloc
and sfree
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct meta {
void (*dtor)(void *);
void *ptr;
};
static struct meta *get_meta(void *ptr) {
return ptr - sizeof (struct meta);
}
__attribute__((malloc))
void *smalloc(size_t size, void (*dtor)(void *)) {
struct meta *meta = malloc(sizeof (struct meta) + size);
*meta = (struct meta) {
.dtor = dtor,
.ptr = meta + 1
};
return meta->ptr;
}
void sfree(void *ptr) {
if (ptr == NULL)
return;
struct meta *meta = get_meta(ptr);
assert(ptr == meta->ptr); // ptr shall be a pointer returned by smalloc