Scoped allocations in C with __attribute__((cleanup))
This is maybe the best thing I’ve discovered in working with modern c. I know that systemd uses it a lot, but it’s still worth writing about.
First, the tldr:
void scoped_close(int *fd)
{
if (*fd != -1)
{
close(*fd);
}
}
int foo(void)
{
int fd __attribute__((cleanup(scoped_close))) = open('test.txt', O_RDONLY);
...
// scoped_close(&fd) is called -> fd is automatically closed on end of scope
}
The cleanup attribute is supported in both gcc and clang.
It is a variable attribute that applies a function to the variable when it goes out of scope. The function gets passed a pointer to the variable.
What it avoids
You’ll never again have to worry about properly freeing resources. Files, sockets, directories, memory allocations, all can be taken care of cleanly. You don’t have to track exit points, and can just hard return or break in the middle, and allocations will be taken care of.
But what if you want to return allocated info?
void * bar()
{
void * __attribute__((cleanup((freep))) buf= malloc(40);
...
if (something_wrong())
{
return NULL;
}
...
// swap out the buffer for a NULL to avoid the cleanup
void * out = buf;
buf = NULL;
return out;
}
systemd even has helpful macros
#define TAKE_PTR(ptr) ({ \
void *__ptr = (ptr); \
(ptr) = NULL; \
__ptr; \
})
#define TAKE_FD(fd) ({ \
int __fd = (fd); \
(fd) = -1; \
__fd; \
})
Some great scope helpers
I personally like at least the following. But there’s endless possibilities here.
// TODO MUTEX_LOCKED
#pragma once
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
static inline void scoped_close(int *fd)
{
if (*fd != -1)
{
close(*fd);
*fd = -1;
}
}
static inline void scoped_free(void **ptr)
{
if (*ptr != NULL)
{
free(*ptr);
*ptr = NULL;
}
}
static inline void scoped_fclose(FILE **f)
{
if (*f != NULL)
{
fclose(*f);
*f = NULL;
}
}
#define scoped_fd int __attribute__((cleanup(scoped_close)))
#define scoped_alloc void * __attribute__((cleanup(scoped_free)))
#define scoped_file FILE * __attribute__((cleanup(scoped_fclose)))
Conclusion
The cleanup attribute can totally change the way you write C.