| Issue |
169011
|
| Summary |
Clang Support Nested Functions For C
|
| Labels |
clang
|
| Assignees |
|
| Reporter |
mcmah309
|
Clang currently does not support nested functions for c, while gcc does. Nested functions have a variety of uses, but most useful I believe is it allows creating a defer macro that runs code at the end of the scope. This is exactly like zig's `defer` statement and brings this power to c.
e.g.
```c
#define __DEFER_CAT_IMPL(a, b) a##b
#define __DEFER_CAT(a, b) __DEFER_CAT_IMPL(a, b)
#define __DEFER_UNIQUE_ID(prefix) __DEFER_CAT(prefix, __LINE__)
#define __DEFER_IMPL(cleanup_statement, unique_name) \
auto void unique_name(void *p __attribute__((unused))) { \
cleanup_statement; \
} \
char __DEFER_CAT(__defer_var_, unique_name) __attribute__((cleanup(unique_name)))
#define DEFER(cleanup_statement) \
__DEFER_IMPL(cleanup_statement, __DEFER_UNIQUE_ID(__defer_func_))
```
Full code example
```c
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Allocator Allocator;
struct Allocator {
void* (*alloc)(Allocator* self, size_t size);
void (*free)(Allocator* self, void* ptr);
void* context; // For allocator-specific data
};
void* default_alloc(Allocator* self, size_t size) {
(void)self;
return malloc(size);
}
void default_free(Allocator* self, void* ptr) {
(void)self;
free(ptr);
}
Allocator default_allocator = {
.alloc = default_alloc, .free = default_free, .context = NULL};
//************************************************************************//
typedef struct {
char* name;
int horsepower;
} Engine;
void engine_init(Engine* self, const char* name, int hp, Allocator* alloc) {
size_t len = strlen(name) + 1;
self->name = alloc->alloc(alloc, len);
strcpy(self->name, name);
self->horsepower = hp;
}
void engine_deinit(Engine* self, Allocator* alloc) {
alloc->free(alloc, self->name);
self->name = NULL;
}
void engine_print(Engine* self) {
printf(" Engine: %s (%d HP)\n", self->name, self->horsepower);
}
typedef struct {
Engine engine;
char* model;
int year;
} Car;
void car_init(Car* self, const char* model, int year, const char* engine_name,
int hp, Allocator* alloc) {
size_t len = strlen(model) + 1;
self->model = alloc->alloc(alloc, len);
strcpy(self->model, model);
self->year = year;
engine_init(&self->engine, engine_name, hp, alloc);
}
void car_deinit(Car* self, Allocator* alloc) {
engine_deinit(&self->engine, alloc); // destroy() - no free
alloc->free(alloc, self->model);
self->model = NULL;
}
void car_print(Car* self) {
printf("Car: %d %s\n", self->year, self->model);
engine_print(&self->engine);
}
//************************************************************************//
#define __DEFER_CAT_IMPL(a, b) a##b
#define __DEFER_CAT(a, b) __DEFER_CAT_IMPL(a, b)
#define __DEFER_UNIQUE_ID(prefix) __DEFER_CAT(prefix, __LINE__)
#define __DEFER_IMPL(cleanup_statement, unique_name) \
auto void unique_name(void *p __attribute__((unused))) { \
cleanup_statement; \
} \
char __DEFER_CAT(__defer_var_, unique_name) __attribute__((cleanup(unique_name)))
#define DEFER(cleanup_statement) \
__DEFER_IMPL(cleanup_statement, __DEFER_UNIQUE_ID(__defer_func_))
//************************************************************************//
int main(void) {
printf("outer scope start\n");
{
Car* car = default_allocator.alloc(&default_allocator, sizeof(Car));
car_init(car, "Mustang", 2024, "V8", 450, &default_allocator);
DEFER({
car_deinit(car, &default_allocator);
default_allocator.free(&default_allocator, car);
printf("Deinit car\n");
fflush(stdout);
});
car_print(car);
}
printf("outer scope end\n");
}
```
Output with gcc
```console
outer scope start
Car: 2024 Mustang
Engine: V8 (450 HP)
Deinit car
outer scope end
```
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs