Emulating "defer" in C, with Clang or GCC+Blocks Emulating "defer" in C, with Clang or GCC+Blocks

Posted by smokris on 2015.10.08 @ 23:24

Filed under:

In the Go language (and now Apple’s Swift), there’s a new control-flow mechanism: defer.

When you tag a function call with the defer keyword, its execution is deferred until the end of the current scope. This makes it easy to construct an ad-hoc use of the RAII pattern — handy for placing resource initialization and finalization next to each other, to make it less likely that you’ll forget to finalize a resource.

I was curious whether it was possible to do something similar in C. It turns out it’s pretty straightforward.

Go’s canonical example of using defer is error-handling when opening two files. Here’s their (intentionally sloppy) example, translated into C:

FILE *a = fopen("a.txt", "r");
if (!a)
    return;
 
FILE *b = fopen("b.txt", "r");
if (!b)
    return;
 
…

This is sloppy since, if a.txt exists but b.txt doesn’t, a file pointer is leaked.

By consolidating initialization and finalization using defer, the bug vanishes:

FILE *a = fopen("a.txt", "r");
if (!a)
    return;
defer ^{ fclose(a); };
 
FILE *b = fopen("b.txt", "r");
if (!b)
    return;
defer ^{ fclose(b); };
 
…

Now, there are 3 possible routes:

  1. a.txt doesn’t exist — the first return is performed; no files are closed.
  2. a.txt exists but b.txt doesn’t — fclose(a) is pushed onto the defer stack, and it’s automatically executed just before the second return is performed.
  3. a.txt and b.txt exist — both fclose(a) and fclose(b) are pushed onto the defer stack, and are automatically executed later on.

To use this, I just need to add a few lines of code:

static inline void defer_cleanup(void (^*b)(void)) { (*b)(); }
#define defer_merge(a,b) a##b
#define defer_varname(a) defer_merge(defer_scopevar_, a)
#define defer __attribute__((cleanup(defer_cleanup))) void (^defer_varname(__COUNTER__))(void) =

How does the magic work? Starting from the last line:

  1. __attribute__((cleanup(…))) — an extension to the C language, provided by GCC and Clang. When a variable is tagged with this attribute, the specified function is invoked when the variable leaves scope.
  2. void (^…)(void) = ^{ … } — C Blocks, an extension to the C language, provided by Clang or GCC with Apple’s Blocks patch. Allows you to define inline, anonymous functions. In this case, I’m taking the block that’s expected to appear after the defer macro and assigning it to a local variable.
  3. a##b — merge two strings into a single token. By combining defer_scopevar_ with the __COUNTER__, I get a unique variable name.
  4. void defer_cleanup(void (^*b)(void)) { (*b)(); } — a C function that takes a block as its parameter. I pass this function to __attribute__((cleanup(…))), and it gets invoked when the variable (defined in step #2) leaves scope. When the function is invoked, it just calls the block that was passed to it. (This indirection is necessary since __attribute__((cleanup(…))) doesn’t directly support C Blocks.)

There you have it. defer — and all the code cleanliness it offers — in C.

You should provide an example of how all these macros get expanded into the source code so it’s slightly easier to understand.

@Anonymous: __attribute__((cleanup(defer_cleanup))) void (^defer_scopevar_0)() = ^{ fclose(a); }; - just run gcc with -E.

This is awesome, thank you. I did get “unused variable” warnings, but these go away when you change the attribute to “__attribute((unused, cleanup …”.