Will execute the code stated right before exiting a function
#include <stdio.h>
int main() {
defer printf("This will print after OK\n");
printf("OK\n");
// OK
// This will print after OK
return 0;
}#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
srand(time(NULL));
FILE *f = fopen("test.txt", "w");
defer {
fclose(f);
printf("File closed!\n");
}
/* 50% change to exit early with error */
if ((rand() & 1) == 0) {
printf("No need to call fclose(), because the defer executes before the return.\n");
return 1;
}
fprintf(f, "Hello world!\n");
return 0;
}#include <stdio.h>
#include <stdlib.h>
#include <SDL.h>
static SDL_Window *game_window;
static SDL_Renderer *game_renderer;
int main() {
/* Init SDL */
if (SDL_Init(SDL_INIT_VIDEO) != 0)
return 1;
defer SDL_Quit();
/* Create window */
game_window = SDL_CreateWindow("Defer test",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
640, 480, SDL_WINDOW_SHOWN);
if (!game_window)
return 2;
defer SDL_DestroyWindow(game_window);
/* Create renderer */
game_renderer = SDL_CreateRenderer(game_window, -1,
SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
if (!game_renderer)
return 3;
defer SDL_DestroyRenderer(game_renderer);
/*
* This example shows a basic usage of defer.
* If function returns early, it will execute the defer statements before
* exiting the function.
* I.E.:
* - If game_renderer is NULL, SDL_DestroyWindow and SDL_Quit will be called
* (in that order, reverse to declaration), but SDL_DestroyRenderer won't.
* Because the return happens before the defer is declared.
*
* [!!!]
* Beware that defer statements are related to the function, not the block of code.
* This means that even though the following if statement never runs,
* the following defer is declared and will beexecuted because `main`
* does not return before the defer is declared.
*/
if (1 == 0) {
defer printf("Actual defer execution\n");
return 4;
}
printf("OK\n");
return 0;
}Will execute the code stated right before the break/continue label of a for/while/do loop. At first glance it can look totally unnecessary, but for memory management escenarios can be very useful.
#include <stdio.h>
int main() {
for (int i = 0; i < 3; i++) {
defer printf("This will run when the functions is over. Nothing to do with the loop\n");
defer break printf("[%d] This will run right before loop break\n", i);
defer continue printf("[%d] This will run right before loop increment\n", i);
}
printf("OK\n");
// [0] This will run right before loop increment
// [1] This will run right before loop increment
// [2] This will run right before loop increment
// [3] This will run right before loop break
// OK
// This will run when the functions is over. Nothing to do with the loop
return 0;
}Extends a type with implicit function calls. It is equivalent of passing the caller as the first argument.
- Function is registered as
(type).name(the symbol is mangled, see symbols). - I.E.
mycat.meow()is exactly the same to(Cat).meow(mycat).
#include <stdio.h>
int (int a) sum(int b) {
return a + b;
}
int main() {
// = 15
printf("= %d\n", ((int)10).sum(5));
// = 15
printf("= %d\n", (int).sum(10, 5));
return 0;
}#include <stdio.h>
struct color {
unsigned char r, g, b, a;
};
typedef struct Car Car;
struct Car {
char *name;
struct color col;
int kms;
};
void (Car *c) drive(int kms) {
c->kms += kms;
}
int main() {
Car c = {
.name = "Kachow",
.col = {
.r = 255,
.g = 0,
.b = 0,
.a = 255,
},
.kms = 3,
};
(&c).drive(100);
Car *p = &c;
p.drive(100);
// Kachow has driven 203 kms
printf("%s has driven %d kms\n", c.name, c.kms);
return 0;
}Will break out of a loop or switch N levels up.
Technically this can be done already in C with a goto statement, but this is syntactically more readable.
#include <stdio.h>
int main() {
for (int j = 0; j < 10; j++) {
for (int i = 0; i < 10; i++) {
if (i == 5 && j == 2)
break 2; /* Break both for loops */
printf(" %d\n", i);
}
putchar('\n');
}
printf("\n -- End --\n");
// 0 1 2 3 4 5 6 7 8 9
// 0 1 2 3 4 5 6 7 8 9
// 0 1 2 3 4
// -- End --
return 0;
}Allows the user to change the output symbol of a variable or function.
- New attribute
__attribute__((symbol("new_symbol_name")))-> changes the output symbol at assembly level of the variable or function. - New keyword
symbolof(identifier)-> returns the expected symbol name of the variable or function as a string literal.
#include <stdio.h>
int foo __attribute__((symbol("bar"))) = 123;
int bar2 = 456; // No symbol change.
int sum(int a, int b) __attribute__((symbol("abc")));
int (int a) sum(int b) __attribute__((symbol("def"))); // was "sum$i"
// void abc(); // Link error: redefinition of symbol 'abc'
asm(".global __asm_var__\n"
".data\n.type __asm_var__, @object\n"
".size __asm_var__, 8\n.align 8\n"
"__asm_var__: .long 32\n");
extern long ext1 __attribute__((symbol("__asm_var__")));
void test_symbol(char *a, char *b) {
printf("%s == %s -> %d\n", a, b, strcmp(a, b) == 0);
}
int main(void) {
test_symbol(symbolof(foo), "bar");
test_symbol(symbolof(bar2), "bar2");
test_symbol(symbolof(sum), "abc");
test_symbol(symbolof((int).sum), "def");
test_symbol(symbolof(ext1), "__asm_var__");
printf("ext1 = %d\n", ext1);
// bar == bar -> 1
// bar2 == bar2 -> 1
// abc == abc -> 1
// def == def -> 1
// __asm_var__ == __asm_var__ -> 1
// ext1 = 32
// OK
printf("OK\n");
return 0;
}Symbol mangling can be used to create syntax aliases to existing variables and functions in the compiled binary.
Sure you can do #define def abc, but you cannot use preprocessor for Type methods, so this is a useful feature of the symbol mangling.
#include <stdio.h>
#include <string.h>
/* Silly GCC headers
* destroy __attribute__ */
#ifdef __attribute__
#undef __attribute__
#endif
int abc = 32;
/* def is an alias of abc does not create a new variable */
extern int def __attribute__((symbol("abc")));
/* Use symbol mangling to create a function alias string.concat -> strcat */
char *(char *s1) concat(char *s2) __attribute__((symbol("strcat")));
int main(void) {
char str_hello[100] = "Hello";
str_hello.concat(" World").concat("!")
printf("%d\n%s\n", def, str_hello);
// 32
// Hello World!
return 0;
}#include <stdio.h>
/*
* Question: How can I alias an inline function?
* Answer: Inline the inliner
*/
inline int inline1(int n) { return n + 3; }
inline int (int i) inline2() { return inline1(i); }
/* Since both functions are inlined,
* the binary result is the same as
* calling just inline1(), so this
* is an alias of inline1() */
int main(void) {
/*
binary result is exactly the same as
printf("%d\n", 10 + 3);
so this is an inline function alias
*/
printf("%d\n", ((int)10).inline2());
// 13
return 0;
}Please note that the syntax of all the planned features is subject to change.
You can map an operator (+, +=, etc) to a type method call.
This can be very useful to avoid a headache when working with complex data types like strings and arrays.
But this is one of the features that could kill a C succesor, and has so many detractors because of the hidden code; so it has to be implemented wisely.
There are some rules that the compiler follows:
- You cannot overload primitive types.
1+1is 2, and not a function call - You cannot pass a number to a pointer overload.
This is meant to preserve pointer arithmetic.
(char*)"Hello" + 2is"llo", and not a function call
| Operator | Method |
|---|---|
+ |
__add__(self, other) |
- |
__sub__(self, other) |
* |
__mul__(self, other) |
/ |
__div__(self, other) |
% |
__mod__(self, other) |
| Operator | Method |
|---|---|
< |
__lt__(self, other) |
> |
__gt__(self, other) |
<= |
__le__(self, other) |
>= |
__qe__(self, other) |
== |
__eq__(self, other) |
!= |
__ne__(self, other) |
| Operator | Method |
|---|---|
+= |
__iadd__(self, other) |
-= |
__isub__(self, other) |
*= |
__imul__(self, other) |
/= |
__idiv__(self, other) |
%= |
__imod__(self, other) |
| Operator | Method |
|---|---|
- |
__neg__(self) |
+ |
__pos__(self) |
~ |
__inv__(self) |
#include <stdio.h>
#include <string.h>
/* char* + char* ====> s1.__iadd__(s2) */
char *(char *s1) __iadd__(char *s2) __attribute__((symbol("strcat")));
int main(void) {
char str_hello[100] = "Hello";
str_hello += " World";
str_hello += "!";
printf("%s\n", str_hello);
// Hello World!
return 0;
}#include <stdio.h>
#include <string.h>
typedef struct Point Point;
struct Point {
int x;
int y;
};
/* Point + Point ====> p1.__add__(p2) */
Point (Point p1) __add__(Point p2) {
/* structs are copied by value,
* so we could have use p1 directly
* without overwriting the original
* p1 struct */
Point p3;
p3.x = p1.x + p2.x;
p3.y = p1.y + p2.y;
return p3;
}
bool (Point p1) __eq__(Point p2) {
return p1.x == p2.x && p1.y == p2.y;
}
int main(void) {
Point p1 = {1, 2};
Point p2 = {3, 4};
Point p3 = p1 + p2;
printf("%f %f\n", p3.x, p3.y);
// 4 6
return 0;
}The nullish coalescing operator is a ternary operator that returns its right operand if the left operand is null, and its left operand otherwise.
I.E: a ?? b is equivalent to a ? a : b**
#include <stdio.h>
#include <string.h>
char *str_en_hello = "Hello";
char *str_es_hello = "Hola";
int main(void) {
str_en_hello = NULL;
printf("%s\n", str_en_hello ?? str_es_hello);
// Hola
return 0;
}Lambdas are anonymous functions that can be assigned to variables, or used immediately.
#include <stdio.h>
int main() {
auto add = int (int a, int b) {
return a + b;
}
// 5
printf("%d\n", add(2, 3));
return 0;
}#include <stdio.h>
void sort(int *a, int n, int (*cmp)(int, int)) {
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (cmp(a[i], a[j]) > 0) {
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
}
}
int main() {
/* Unordered array */
int a[10] = { 4, 1, -5, 1, 3, 2, 6, 8, 9, 7 };
sort(a, 10, int (int a, int b) {
/* Return 1 if a > b */
return a - b;
});
/* Print out the ordered array */
for (int i = 0; i < 10; i++)
printf("%d ", a[i]);
putchat('\n');
return 0;
}Forked from chibicc.
Tip for debugging with gdb.
File ~/.gdbinit:
set disable-randomization on
set follow-fork-mode child
catch syscall exit
catch syscall exit_group
catch signal SIGSEGV
catch signal SIGABRT
break error