defer
for Objective-C
Update
Predictably, Peter Steinberger and Matej Bukovinski beat me to this, and Justin Spahr-Summers was ahead of them.
This all started because I was complaining about some uninitialized pointer value causing me grief1 and someone (explicitly trolling) said they always check pointers using:
int fds[2] = { -1, -1};
pipe(fds);
if (write(fds[0], pointer_to_check, sizeof(intptr_t)) == -1) {
close(fds[0]);
close(fds[1]);
return not_valid;
} else {
close(fds[0]);
close(fds[1]);
return valid;
}
In case it’s not abundantly clear, you should never do this2, but of course the first thing I saw was the duplication of code responsible for managing resources, a reminder of how redundant and error-prone C can be.
A different formulation of this code might look like:
int rc, fds[2] = { -1, -1};
pipe(fds);
if (write(fds[0], pointer_to_check, sizeof(intptr_t)) == -1) {
rc = not_valid;
} else {
rc = valid;
}
close(fds[0]);
close(fds[1]);
return rc;
This reduces duplication, but has worse locality. I don’t love it, but I feel like it’s the safer style.
Really what I want is something like Swift’s defer
:
int fds[2] = { -1, -1};
pipe(fds);
defer ^{
close(fds[0]);
close(fds[1]);
};
if (write(fds[0], pointer_to_check, sizeof(intptr_t)) == -1) {
return not_valid;
} else {
return valid;
}
Turned out it’s not too heinous to hack together, it produces efficient assembly, and it’s even exception-safe!
Here it is:
__attribute__((unused))
static void __defer_cleanup(void (^*pBlock)(void)){ (*pBlock)(); }
#define __defer_tokenpaste(prefix, suffix) prefix ## suffix
#define __defer_blockname(nonce) __defer_tokenpaste(__defer_, nonce)
/* Declare a local block variable with a unique name that contains
* the cleanup code. It has three attributes:
* unused: because you should NEVER touch this local yourself
* deprecated: because you should NEVER touch this local yourself
* cleanup: to get its pointer passed to __defer_cleanup (above)
* when the scope ends
*/
#define defer \
void (^ __defer_blockname(__LINE__))(void) \
__attribute__((unused, \
deprecated("hands off!"), \
cleanup(__defer_cleanup) \
)) =