Background and motivation
Currently, there are 2 main ways to have a “generic” way to automatically cleanup at out of scope depending on the type of the variable:
#include <stdio.h>
#define $automatic(T) [[gnu::cleanup(T##_cleanup)]] T
static void int_cleanup(int *i)
{
printf("%p: %d\n", i, *i);
}
int main()
{
$automatic(int) i = 10;
}
or
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#define $automatic [[gnu::cleanup(deallocate)]]
struct Allocation {
void (*free)(void *);
uint8_t data[];
};
void *allocate(size_t size, void (*free)(void *))
{
struct Allocation *allocation = malloc(sizeof(struct Allocation) + size);
allocation->free = free;
return allocation->data;
}
void deallocate(void *ptr)
{
struct Allocation *allocation = (*(void **)ptr) - sizeof(struct Allocation);
allocation->free(allocation->data);
free(allocation);
}
static void int_cleanup(int *i)
{
printf("%p: %d\n", i, *i);
}
int main()
{
$automatic int *i = allocate(sizeof(int), (void(*)(void *))int_cleanup);
*i = 42;
}
The first solution requires a macro to concat a type to make an identifier, which wouldnt work for types which aren’t valid identifiers, i.e struct MyStruct
or int *
.
The second solution isn’t very type-safe, and requires an allocation and a fat pointer.
Clang has [[clang::overloadable]]
in order to bring C+±like overloading to C
#include <stdio.h>
[[clang::overloadable]]
static void print(const char *str)
{
printf("%s\n", str);
}
[[clang::overloadable]]
static void print(int num)
{
printf("%d\n", num);
}
[[clang::overloadable]]
static void print(float num)
{
printf("%f\n", num);
}
int main()
{
print("Hello, World!");
print(42);
print(3.14f);
}
Right now it is an error to use [[clang::overloadable]]
alongside [[gnu::cleanup]]
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#define $automatic [[gnu::cleanup(cleanup)]]
[[clang::overloadable]]
static void cleanup(const char *s)
{
printf("(const char *) %p: \"%s\"\n", s, s);
}
[[clang::overloadable]]
static void cleanup(int *i)
{
printf("(int) %p: %d\n", i, *i);
}
[[clang::overloadable]]
static void cleanup(float *i)
{
printf("(float) %p: %f\n", i, *i);
}
[[clang::overloadable]]
static void cleanup(void *p)
{
printf("(void *) %p\n", p);
}
int main()
{
$automatic int i = 4;
$automatic float f = 4.2;
$automatic const char *s = "Hello, World!";
$automatic double d = 4;
}
This results in
error: 'cleanup' argument 'cleanup' is not a single function
Proposed Solution
Allow for cleanup
to deduce which cleanup function to call by the type of the variable. void *
should be a “wildcard” for any non-overloaded cleanup types.
With the proposed solution, the above code should output:
(int) <some address>: 4
(float) <some address>: 4.200000
(const char *) <some address>: "Hello, World"
(void *) <some address>
Rationale
This would make creating safe cleanup for types much easier to use and create, for example:
//I only need to overload `cleanup` in order to create a type which would automatically deallocate with this system
#define $automatic [[gnu::cleanup(cleanup)]]
struct MyStruct {
int i;
float f;
};
[[clang::overloadable]]
static void cleanup(struct MyStruct *s)
{
printf("(struct MyStruct) %p: { .i = %d, .f = %f }\n", s, s->i, s->f);
}
int main()
{
$automatic struct MyStruct s2 = { .i = 4, .f = 3.14 };
}