I’ve been doing a lot of programming in C recently. Part of what comes with that is a lot of manual memory management. GCC and Clang have something called the cleanup attribute (among many other cool attributes that can be found here) that can be applied to variables. When a variable exits scope, a function will be implicitly called that was registered at variable declaration. That way you never have to explicitly deallocate or clean up anything later on. This is very much like how destructors work in C++. Here is an example from the otter codebase.

  OTTER_CLEANUP(otter_target_free_p)
  otter_target *otter_array_tests = otter_target_create(
      "otter_array_tests.so", allocator, filesystem, logger,
      "otter_array_tests.c", "otter_array.h", "otter_test.h", NULL);

I’ve made a couple usability macros that will help if I ever need portability between compilers that express cleanup attributes differently than GCC and Clang. Right now, it is just:

#define OTTER_CLEANUP(func) __attribute__((cleanup(func)))

The only annoying thing about the cleanup attribute is that it wants a function signature that accepts a pointer to the variable type being freed. So, in our example of cleaning up a variable of type otter_target*, you need to provide a void function with one argument accepting a otter_target**. This… is not how I like defining my free functions. I generally have functions that look like this in order to free a specific struct type:

void otter_target_free(otter_target *target);

So we need another function that can just forward to otter_target_free. Luckily, it’s an easy macro to write so that you don’t have to manually create such a boring function. The systemd codebase has a macro that I was able to use for this and can be found here. Here is my version of it:

#define OTTER_DEFINE_TRIVIAL_CLEANUP_FUNC(type, func)                          \
  static inline void func##_p(type *p) {                                       \
    if (*p) {                                                                  \
      func(*p);                                                                \
    }                                                                          \
  }                                                                            \
  struct otter_useless_struct_to_allow_trailing_semicolon

So, the previous declaration of otter_target_free can now become:

void otter_target_free(otter_target *target);
OTTER_DEFINE_TRIVIAL_CLEANUP_FUNC(otter_target*, otter_target_free);

One response to “The “cleanup” attribute is pretty neat”

  1. […] The otter_allocator struct contains a pointer to a vtable of function pointers. Right now, the vtable supports malloc, realloc, free (the obvious three), and a free_allocator function for when the allocator itself is freed/released. As an aside, this pointer to vtable layout is very much how a C++ class with virtual functions works under the hood. I’ve also created some convenience functions to make calling these function pointers a bit easier. If you are curious about OTTER_DEFINE_TRIVIAL_CLEANUP_FUNC, check out my other post on it from a couple of weeks ago. […]

Leave a Reply

Discover more from Nathaniel Wright

Subscribe now to keep reading and get access to the full archive.

Continue reading