I tend to follow a standard idiom when writing C functions
which allocate resources. At the top, I define a variable
for each resource, and initialize it to a sentinel value
meaning that it’s not yet active, and another for the
function’s return value. At the bottom, I define a label
end
, where I release all of the resources which still seem
live, and return the right result. If something goes wrong
partway through, I set the return value to indicate the
problem, and goto end
. Easy.
But it means that the code to release a thing isn’t near where it’s defined or allocated.
I had a brainwave the other day, and wrote a macro.
#define FINALLY(body) \
__extension__ __inline__ TMP(finally_fn) \
(const int __attribute__((unused)) *hunoz) { body } \
int __attribute__((unused, cleanup(TMP(finally_fn)))) \
TMP(finally_var)
The TMP
macro decorates an identifier so that it’s (more or
less) private to the macro invocation. Now I can say
something like
void *p = 0; FINALLY({ free(p); });
and I can use return
as usual for an early exit. Yay.
(Apparently I’m not the only person who’s thought of this.)
This macro has two problems.
Firstly, it uses nested functions, so it only works with GCC. Clang doesn’t support these, presumably because there’s no good way to implement a pointer to a nested function.
Secondly, the syntax is ugly, involving parentheses and a final semicolon.
I can’t figure out a way to solve both problems at the same time.
Solving the former is tricky. I can’t see any way to do
this using actual standard C. There’s a
proposal to add Golang-ish defer
to C
but this (a) isn’t part of the language yet, and (b) is
deeply wrongheaded — most particularly, the proposal’s
authors don’t think it’s a terrible idea to perform all of
the defer
red actions at exit
time. Apparently, they
think it’s really important
to clean the floors before demolishing the building.
If we can’t do this in a standard way, then maybe we can at least make this work with Clang. The best approach I’ve seen is this StackMumble answer which (ab?)uses Clang’s support for Objective C-ish blocks.
static __inline__ void _finally__runblk(void (^*blk)(void))
{ (*blk)(); }
#define FINALLY(body) \
void __attribute__((unused, cleanup(_finally__runblk))) \
(^TMP(finally_blk))(void) = ^{ body }
Yikes! Now just add -fblocks -lBlocksRuntime
to the
compiler command line and you’re done.
This is nowhere near as good as the GCC version. GCC
generates identical machine code for FINALLY
and goto end
code, except for internal label names, so it’s obviously doing
everything right. Clang introduces a whole lot of extra
machinery which it apparently can’t see its way through
optimizing away, so cleanup becomes rather less efficient.
Oh, well. I’m not going to lose much sleep if Clang ends
up looking second-class here.
I can also see a way to eliminate the parentheses:
essentially, you just put the function body last. In GCC,
this means we need a forward-declaration of a nested
function, and that needs a trick to express, because if you
just say it normally, it looks like a local declaration of
a static or external function. The trick is to say
auto
here, because… no, I’ve got nothing.
#define FINALLY \
__extension__ auto void TMP(finally_fn)(const int *); \
int __attribute__((unused, cleanup(TMP(finally_fn))))
TMP(finally_var); \
__extension__ __inline__ void TMP(finally_fn) \
(const int __attribute__((unused)) *hunoz)
Yay!
void *p = 0; FINALLY { free(p); }
Can we do the same with the Clang version? Yes… almost.
#define FINALLY \
void __attribute__((unused, cleanup(_finally__runblk))) \
(^TMP(finally_blk))(void) = ^
Alas, here we need
void *p = 0; FINALLY { free(p); };
with the final ;
. This is acceptable to GCC under C99
rules, but not with C90 rules, which is a shame because I
actually like having a function’s variables all declared
in one block at the top.
Suggestions welcome.
Leave a comment