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 …”.

Yes, this is awesome (mixing blocks, pointer to block, and cleanup attribute), but, unfortunately, it will not work if resources are allocated after the defer body is defined, which makes the defered execution useless.

From http://clang.llvm.org/docs/BlockLanguageSpec.html: - Unlike functions, in addition to executable code, Blocks also contain various variable bindings to automatic (stack), global or managed (heap) memory. - Local automatic (stack) variables referenced within the compound statement of a Block are imported and captured by the Block as const copies. - The capture (binding) is performed at the time of the Block literal expression evaluation.

As an illustration, the subsequent code contains a memory leak:

#define _XOPEN_SOURCE
#define _XOPEN_SOURCE_EXTENDED
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#define defer_merge(a,b) a##b
#define defer_varname(a) defer_merge(defer_scopevar_, a)
#define defer __attribute__((__unused__ __cleanup__(defer_cleanup))) void (^defer_varname(__COUNTER__))(void) = ^
 
static inline void
defer_cleanup (void (^*b) (void))
{
  (*b) ();
}
 
#define destructor(destructor) __attribute__((__cleanup__(destructor)))
 
static void
fcloser (FILE ** f)
{
  if (*f)
    fclose (*f);
}
 
static void
sliberator (char **ps)
{
  if (*ps)
    printf ("Freeing %s\n", *ps);
  free (*ps);
}
 
int
main (void)
{
  FILE *a = fopen ("a.txt", "r");
 
  if (!a)
    return EXIT_FAILURE;
  defer
  {
    fclose (a);
  };
 
  FILE *b destructor (fcloser) = fopen ("b.txt", "r");
 
  if (!b)
    return EXIT_FAILURE;
 
  char *sa = 0;
 
  // sa is imported and captured by the Block as const copies at the time of the defer body evaluation.
  defer
  {
    if (sa)
      printf ("Freeing %s\n", sa);
    free (sa);
  };
  sa = strdup ("Hello a");      // memory leak here.
  // The compiler is not required to capture a variable if it can prove that no references to the variable will actually be evaluated. So:
  printf ("%s\n", sa);
 
  char *sb destructor (sliberator) = 0;
 
  sb = strdup ("Hello b");      // no memory leaks here
  printf ("%s\n", sb);
 
  printf ("Exiting...\n");
  return EXIT_SUCCESS;
}
variables referenced within the compound statement of a Block are imported and captured by the Block as const copies

Oh, good point. Thanks for clarifying that.