Defer in C
About
I came across a post about defer.h on Discord today. And I just fell love in it! I was always curious how to achieve defer
in C. In C++, we can use destructor. In Golang, it is provided out of box. Let’s see what I learnt from this tiny header file.
See an example program
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define DEFER_IMPL
#include "defer.h"
#include <stdlib.h>
#include <stdio.h>
int* create_my_int() { return malloc(sizeof(int)); }
void destroy_my_int(int* i) { free(i); }
int main(void) {
int* i = create_my_int();
defer({
destroy_my_int(i);
});
printf("%d\n", i);
// defer block is executed here, freeing i
}
Using gcc with -E
option, the code after pre-processing is below.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
extern inline void do_defer(defer_block* ptr);
extern inline void do_defer(defer_block* ptr) {
(*ptr)();
}
int* create_my_int() {
return malloc(sizeof(int));
}
void destroy_my_int(int* i) {
free(i);
}
int main(void) {
int* i = create_my_int();
defer_block __attribute__((unused)) __attribute((cleanup(do_defer))) __defer0 = (
{ void __fn__(void) { destroy_my_int(i); }; __fn__; }
);
printf("%d\n", i);
}
One side note about MacOS. MacOS provides gcc command if you installed xcode or llvm. But this is not the GNU gcc. it is a wrapper on top of clang. To test GNU gcc, you need to install it from brew. Also, the path to it is /opt/homebrew/Cellar/gcc/13.2.0/bin/gcc-13
on my Macbook.
First, __attribute(cleanup(do_defer))
means variable __defer0
has a destructor called do_defer
. It will be called when __defer0
gets out of scope. This is my first time to see cleanup
attribute.
Second, note the type of __defer0
is a function. And the function is defined on the fly. I fell love into the closure tricks in C.
Third, where does name __defer0
come from? It is __defer##__COUNTER__
. Ah! I learnt a new predefined macro in gcc.