coredump coredump - 1 month ago 5x
C Question

A good and idiomatic way to use GCC and clang __attribute__((cleanup)) and pointer declarations

I think that GCC extension

is a good idea, at least for some cases, but i can't figure out how to use it in a good way. All i'm doing looks still really annoying.

I saw a lot of code doing
#define _cleanup_(x) __attribute__((cleanup(x))
just to type less, but it there a way to pass there a standard function like
, etc?

As I see I can't just write:

__attribute__((cleanup(free))) char *foo = malloc(10);

Because the cleanup callback will receive
pointer, and I have to always write something like:

static void free_char(char **ptr) { free(*ptr); }
__cleanup__((free_char)) char *foo = malloc(10);

That's pretty annoying, and the most annoying part is to define such cleanup functions for all types you need, because obviously you can't just define it for
void **
. What is the best way to avoid these things?


There's a library that builds general-purpose smart pointers (unique_ptr and shared_ptr) on top of __attribute__((cleanup)) here:

It allows you to write higher-level code like this:

#include <stdio.h>
#include <csptr/smart_ptr.h>
#include <csptr/array.h>

void print_int(void *ptr, void *meta) {
    (void) meta;
    // ptr points to the current element
    // meta points to the array metadata (global to the array), if any.
    printf("%d\n", *(int*) ptr);

int main(void) {
    // Destructors for array types are run on every element of the
    // array before destruction.
    smart int *ints = unique_ptr(int[5], {5, 4, 3, 2, 1}, print_int);
    // ints == {5, 4, 3, 2, 1}

    // Smart arrays are length-aware
    for (size_t i = 0; i < array_length(ints); ++i) {
        ints[i] = i + 1;
    // ints == {1, 2, 3, 4, 5}

    return 0;

As for idiomatic, though? Well the above is certainly close to idiomatic C++. Not C so much. The feature is clearly mainly supported in GCC and Clang because they have C++ compilers as well, so they have the option to make use of the RAII machinery in the C frontend at no extra cost; that doesn't make it a great idea to write C-intended-as-C this way. It kinda relies on a C++ compiler being present despite not actually being used.

If it were me, I'd probably investigate implementing autorelease pools, or something similar that can actually be done in pure C at the language level. Depends how quickly you need your resources to be freed; for memory, you usually can live without immediate cleanup.