Post

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.

This post is licensed under CC BY 4.0 by the author.