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

Reply via email to