Cake works as an extension for MSVC on Windows and as an extension for GCC on Linux. This approach makes Cake useful in real and existing programs.
When applicable, Cake uses the same command line options of MSVC and GCC.
Include directories are specified in cakeconf.h file.
On Windows, to manually discover which directories are included, you can run at Visual Studio command prompt the command:
echo %INCLUDE%
To find out what are the directories used by GCC type:
echo | gcc -E -Wp,-v -
Sample of cakeconf.h
#ifdef __linux__
/*
To find the include directories used by GCC type:
echo | gcc -E -Wp,-v -
*/
#pragma dir "/usr/lib/gcc/x86_64-linux-gnu/11/include"
#pragma dir "/usr/local/include"
#pragma dir "/usr/include/x86_64-linux-gnu"
#pragma dir "/usr/include"
#endif
#ifdef _WIN32
/*
To find the include directories used by MSVC,
open Visual Studio Developer Command prompt and type:
echo %INCLUDE%.
Running Cake inside MSVC command prompt uses %INCLUDE% automatically.
*/
#pragma dir "C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.38.33130/include"
#pragma dir "C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.38.33130/ATLMFC/include"
#pragma dir "C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Auxiliary/VS/include"
#pragma dir "C:/Program Files (x86)/Windows Kits/10/include/10.0.22000.0/ucrt"
#pragma dir "C:/Program Files (x86)/Windows Kits/10/include/10.0.22000.0/um"
#pragma dir "C:/Program Files (x86)/Windows Kits/10/include/10.0.22000.0/shared"
#pragma dir "C:/Program Files (x86)/Windows Kits/10/include/10.0.22000.0/winrt"
#pragma dir "C:/Program Files (x86)/Windows Kits/10/include/10.0.22000.0/cppwinrt"
#pragma dir "C:/Program Files (x86)/Windows Kits/NETFXSDK/4.8/include/um"
#endifThe command line cake -autoconfig generates the cake config file.
We can have a cakeconf.h per project and call a more generic cakeconf.h for system includes.
Sample:
yourproject\cakeconf.h
//system includes...etc
#include "C:\Program Files (x86)\cake\cakeconf.h"
//project extra includes
#pragma dir ".\openssl\include"cake [options] source1.c source2.c ...
SAMPLES
cake source.c
Compiles source.c and outputs /[default-target]/source.c
cake -target=X86_msvc source.c
Compiles source.c and outputs C89 code at /X86_msvc/source.c
cake file.c -o file.cc && cl file.cc
Compiles file.c and outputs file.cc then use cl to compile file.cc
-
-I(same as GCC and MSVC) Adds a directory to the list of directories searched for include files -
-no-outputCake will not generate output -
-D(same as GCC and MSVC) Defines a preprocessing symbol for a source file -
-E(same as GCC and MSVC) Copies preprocessor output to standard output -
-o name.c(same as GCC and MSVC) Defines the output name, when we compile a single file -
-dump-tokensOutput tokens before preprocessor -
-Wnumber -Wno-numberEnables or disable warnings. See warnings -
-disable-assertDisable cake extension where assert is a statement. -
-H(same as gcc, /showIncludes in MSVC) Causes the compiler to output a list of the include files. -
-preprocess-def-macropreprocess def macros after expansion -
-WallEnables all warnings -
-sarifGenerates sarif files. Sarif Visual Studio plugin https://marketplace.visualstudio.com/items?itemName=WDGIS.MicrosoftSarifViewer -
-sarif-pathSpecifies the Sarif output dir. "Visual Studio -> External Tools"-Wstyle -msvc-output -no-output -sarif -sarif-path "$(SolutionDir).sarif" $(ItemPath) -
-line-directivesEmmits #line directives -
-targetDefines how the source code is interpreted (integers sizes, align etc) and specifies the C89 output that is compatible with the target compiler. Options: x86_x64_gcc, x86_msvc, x64_msvc, catalina, ccu8 -
-msvc-outputOutput is compatible with Visual Studio IDE. -
-fdiagnostics-color=never(same as GCC) Output will not use colors -
-fanalyzerruns cake flow analysis -
-auto-configGenerates cakeconf.h header (see includes) -
-style=nameSet the style used in (w011) style warnings. Options are-style=cake,-style=gnu,-style=microsoft -
-comment-to-attrConverts at the preprocessor phase, comment like this/*w12*/to attributes[[cake::w12]] -
-const-literalMakes the compiler handle string literals as const char[] rather than char[].
The current backend generates C89-compatible code, which can be pipelined with existing compilers to produce executables. You can create your own C compiler backend.
The output is a simplified version with some K & R and C89.
Using C89 as base:
- no preprocessor
- no typedefs
- no enums
- no const
- no constant expressions
- no switch
- no nested structs/unions
- no sizeof
- no local static variables
- arrays[size], size is always given and it is integer
- function prototypes are generated
For instance:
cake c:\project\file1.coutput:
c:\project
├── file1.c
├── target
├── file1.c
More files..
cake c:\project\file1.c c:\project\other\file2.c
output
c:\project
├── file1.c
├── other
│ ├── file2.c
├── target
├── file1.c
├── other
├── file2.c
https://nvlpubs.nist.gov/nistpubs/Legacy/FIPS/fipspub160.pdf
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n325.pdf https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
#define __STDC_VERSION__ 199901L //C99void f(const char* restrict s);
int main(){
f("");
}try
Currently restrict is being removed on the generated code.
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n683.htm
In C99/C23, there are two related but distinct concepts:
- VLA objects (
int a[n]) - a local array whose storage is allocated on the stack at runtime. Optional in C11/C23 (controlled by__STDC_NO_VLA__). - VM types (
int (*p)[n]) - any type derived from a runtime-sized array, including pointers to VLAs. Mandatory in C23 even when__STDC_NO_VLA__is defined.
Cake supports VM types and converts them to C89-compatible code. Cake does not support VLA objects.
#include <stdlib.h>
#include <stdio.h>
int main() {
int n = 2;
int m = 3;
int (*p)[n][m] = malloc(sizeof *p);
printf("%zu\n", sizeof(*p));
free(p);
}try
#include <stdio.h>
#include <stdlib.h>
struct X {
int count;
double values[]; // flexible array
};
/*
The size of a structure with a flexible array member is
determined as if the flexible array member were omitted,
EXCEPT that it may have more trailing padding than the
omission would imply
*/
int main() {
int n = 3;
printf("sizeof(struct X) = %d\\n", (int) sizeof(struct X));
printf("allocated = %d\\n", (int) sizeof(struct X) + n * sizeof(double));
struct X* p = malloc(sizeof(struct X) + n * sizeof(double));
if (p == NULL) return 0;
p->count = n;
p->values[0] = 10.0;
p->values[1] = 20.0;
p->values[2] = 30.0;
for (int i = 0; i < p->count; ++i)
printf("%f\\n", p->values[i]);
free(p);
return 0;
}try
#include <stdlib.h>
void F(int a[static 5]) {
}
int main()
{
F(0);
F(NULL);
F(nullptr);
int a[] = {1, 2, 3};
F(a);//error
int b[] = { 1, 2, 3 , 4, 5};
F(b);
int c[] = { 1, 2, 3 , 4, 5, 6};
F(c);
}try
Cake will perform the same checks regardless of the static keyword.
Not implemented
Not implemented
double d = 0x1p+1;try
Cake converts hexadecimal floating-point values to decimal floating-point representation using strtod followed by snprintf. This conversion may introduce precision loss.
0x1.234p1 means:
2 3 4
r1= 1 + --- + --- + --- = 1.1376953125
1 2 3
16 16 16
1
1.1376953125 x 2 = 2.275390625 (final number)
struct s {
int i;
};
int f(void) {
struct s * p = 0, * q;
int j = 0;
again:
q = p, p = & ((struct s) { j++ });
if (j < 2) goto again;
return p == q && q -> i == 1;
}try
N716 https://www.open-std.org/jtc1/sc22/wg14/www/docs/n716.htm
int main()
{
int a[6] = {[4] = 29, [2] = 15 };
struct point { int x, y; };
struct point p = { .y = 2, .x = 3 };
}try
N494 https://www.open-std.org/jtc1/sc22/wg14/www/docs/n494.pdf
//line commentsint main()
{
const int max = 10;
for (int n = max - 1; n >= 0; n--)
{
// body of loop
}
}try
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n709.htm (30 May 1997) https://www.open-std.org/jtc1/sc22/wg14/www/docs/n741.htm
inline int sum(int a, int b)
{
return a + b;
}
int main(void)
{
int r = sum(1, 2);
}try
// Use -E option
//6.10.11 Pragma operator
#define LISTING(x) PRAGMA(listing on #x)
#define PRAGMA(x) _Pragma(#x)
LISTING (..listing.dir)
try
N611 13 Sep 96 Mooney, __FUNC__
#include <stdio.h>
int main()
{
printf("%s\n", __func__);
printf("%s\n", __func__);
}try
#include <stdio.h>
#define debug(...) fprintf(stderr, __VA_ARGS__)
int main()
{
int x = 1;
debug("X = %d\n", 1);
}try
N707 https://www.open-std.org/jtc1/sc22/wg14/www/docs/n707.htm
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n815.htm (1998)
int main(void)
{
_Bool b = 1;
return 0;
}try
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2310.pdf
#define __STDC_VERSION__ 201112L //C11https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1330.pdf
int main()
{
_Static_assert(1 == 1, "error");
}try
_Static_assert became static_assert in C23.
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1406.pdf
struct v {
union { /* anonymous union*/
struct { int i, j; }; /* anonymous structure*/
struct { long k, l; } w;
};
int m;
} v1;
int main(){
v1.i = 2; /* valid*/
v1.w.k = 5; /* valid*/
}try
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1478.htm
_Noreturn void f () {
abort(); // ok
}_Noreturn became [[noreturn]] in C23.
try
Thread_local uses __declspec(thread) in MSVC output and __thread with GCC output.
Atomic - not implemented
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1441.htm
#include <math.h>
#define cbrt(X) _Generic((X), \
double: cbrtl, \
float: cbrtf , \
default: cbrtl \
)(X)
int main(void)
{
cbrt(1.0);
}try
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1326.pdf
int i = U'ç';
int i2 = u'ç';try
Important: Cake assume source is utf8 encoded.
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1488.htm
char * s1 = u8"maçã";
char * s2 = u8"maca";try
Important: Cake assume source is utf8 encoded.
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1397.htm
int main()
{
int align = alignof(int);
}try
_Alignof became alignof in C23.
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1335.pdf
Uses __declspec(align(n)) in MSVC output and __attribute__((aligned(n))) in GCC output.
https://open-std.org/JTC1/SC22/WG14/www/docs/n3096.pdf
#define __STDC_VERSION__ 201710L //C17 (Cake accepts C17 source)
#define __STDC_VERSION__ 202311L //C23https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2778.pdf
C23 formally separates two concepts that were bundled together in C99:
- VLA objects (
int a[n]) - arrays with automatic storage duration whose size is determined at runtime. These remain optional in C23. An implementation that defines__STDC_NO_VLA__does not support them. - VM types (
int (*p)[n]) - variably-modified types: any type derived from a runtime-sized array dimension, including pointers to VLAs. These are mandatory in C23. Every conforming implementation must support them regardless of__STDC_NO_VLA__.
The rationale (N2778, Martin Uecker): VM types encode array bounds in the type system, allowing compilers to detect out-of-bounds accesses at compile time or at runtime. They are a form of dependent type and their implementation cost is much lower than stack-allocated VLAs.
/* VM type - mandatory in C23, supported by Cake */
void foo(int n, double (*x)[n])
{
(*x)[0] = 1.0; /* bounds visible in the type */
}In C23, __STDC_NO_VLA__ only indicates that VLA objects with automatic storage
duration are not supported. It does not mean VM types are unavailable.
Cake reflects this split exactly:
- VLA object declarations produce a compile-time error with a suggestion to use a VM type pointer instead.
- VM type pointers, parameters, and
sizeofexpressions are fully supported and translated to C89-compatible code.
See the C99 VM types section for full details and C89 output examples.
Not implemented. https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1107.htm
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1330.pdf
int main(void)
{
static_assert(1 == 2);
}try
https://open-std.org/JTC1/SC22/WG14/www/docs/n2418.pdf
int main(){
unsigned char c = u8'~';
}try
https://www.open-std.org/JTC1/SC22/WG14/www/docs/n2841.htm
int main(){
func(); //this is an error in C23
}try
See also Remove support for function definitions with identifier lists
https://open-std.org/JTC1/SC22/WG14/www/docs/n2432.pdf
Not implemented yet.
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3037.pdf
struct foo { int a; } p;
void bar(void)
{
struct foo { int a; } q;
q = p;
}try
https://open-std.org/JTC1/SC22/WG14/www/docs/n2480.pdf
int f(int );
int f(int ) {
}try
Missing a dummy name when generating c89.
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2626.pdf
int main()
{
int a = 1000'00;
}try
try
#define X 0b1010
int main()
{
int a = X;
int b = 0B1010;
}https://open-std.org/JTC1/SC22/WG14/www/docs/n3042.htm
int main()
{
void * p = nullptr;
auto p2 = nullptr;
typeof(nullptr) p3 = nullptr;
}try
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2935.pdf
#if true
#warning yes..
#endif
int main()
{
bool b = true;
}try
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2900.htm https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3011.htm
int main()
{
struct X {
int i;
} x = {};
x = (struct X) {};
struct Y
{
struct X x;
} y = { {} };
} try
https://open-std.org/JTC1/SC22/WG14/www/docs/n3007.htm
static auto a = 3.5;
auto p = &a;
double A[3] = { 0 };
auto pA = A;
auto qA = &A;try
https://open-std.org/JTC1/SC22/WG14/www/docs/n2927.htm https://open-std.org/JTC1/SC22/WG14/www/docs/n2930.pdf
#define SWAP(a, b) \
do {\
typeof(a) temp = a; a = b; b = temp; \
} while (0)
int main()
{
/*simple case*/
int a = 1;
typeof(a) b = 1;
/*pay attention to the pointer*/
typeof(int*) p1, p2;
/*let's expand this macro and see inside*/
SWAP(a, b);
/*for anonymous structs we insert a tag*/
struct { int i; } x;
typeof(x) x2;
typeof(x) x3;
/*Things get a little more complicated*/
int *array[2];
typeof(array) a1, a2;
typeof(array) a3[3];
typeof(array) *a4[4];
/*abstract declarator*/
int k = sizeof(typeof(array));
/*new way to declare pointer to functions?*/
typeof(void (int)) * pf = nullptr;
}try
TODO
https://open-std.org/JTC1/SC22/WG14/www/docs/n3029.htm
enum a {
a0 = 0xFFFFFFFFFFFFFFFFULL
};
static_assert(_Generic(a0,
unsigned long long: 0,
int: 1,
default: 2 == 0));try
The type of the enum must be adjusted.
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3018.htm
#include <stdio.h>
constexpr int c = 123;
constexpr int c2 = c + 1000;
int a[c];
constexpr double PI = 3.14;
static_assert(PI + 1 == 3.14 + 1.0);
struct Y {
int a;
int ar[3];
int b;
};
void T3()
{
constexpr struct Y y = { .ar[1] = 2, 3, 4 };
static_assert(y.a == 0);
static_assert(y.ar[0] == 0);
static_assert(y.ar[1] == 2);
static_assert(y.ar[2] == 3);
static_assert(y.b == 4);
static_assert(y.ar[1] + y.ar[2] == 5);
}
static_assert("abc"[0] == 'a');
int main()
{
constexpr char ch = 'a';
printf("%f %c", PI, ch);
}
try
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3030.htm
enum X : short {
A
};
int main() {
enum X x = A;
}try
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2335.pdf https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2554.pdf
TODO
https://open-std.org/JTC1/SC22/WG14/www/docs/n2408.pdf
void g(){}
void h(){}
void i(){}
void f(int n) {
void g(void), h(void), i(void);
switch (n) {
case 1: /* diagnostic on fallthrough discouraged */
case 2:
g();
[[fallthrough]];
case 3: /* diagnostic on fallthrough discouraged */
h();
case 4: /* fallthrough diagnostic encouraged */
i();
//[[fallthrough]]; /* constraint violation */
}
}try
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2334.pdf
// Compile with -w03
[[deprecated]] void f2() {}
struct [[deprecated]] S { int a;};
enum [[deprecated]] E1 { one };
int main(void) {
struct S s;
enum E1 e;
f2();
}try
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2270.pdf
//
// Compile with -w02 -w06
//
void f( [[maybe_unused]] int arg1, int arg2) //warning C0006: 'arg2': unreferenced formal parameter
{
[[maybe_unused]] int local1;
int local2; //warning C0002: 'local2': unreferenced declarator
}try
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2267.pdf
https://open-std.org/JTC1/SC22/WG14/www/docs/n2448.pdf
#include <stdlib.h>
struct [[nodiscard]] error_info { int error; };
struct error_info enable_missile_safety_mode(void);
void launch_missiles(void);
void test_missiles(void) {
enable_missile_safety_mode();
launch_missiles();
}
[[nodiscard("must check armed state")]]
bool arm_detonator(int within);
void detonate();
void call(void) {
arm_detonator(3);
detonate();
}try
Cake implementation does not yet support the optional message argument of [[nodiscard("message")]].
TODO
https://open-std.org/JTC1/SC22/WG14/www/docs/n2956.htm
typedef double f_t [[reproducible]] (double); // invalid, applies to identifier f_t
typedef double g_t(double) [[reproducible]]; // valid, applies to type
extern g_t f [[unsequenced]]; // invalid, applies to identifier f
extern typeof(double(double)) [[unsequenced]] g; // valid, applies to type specifier
extern g_t [[unsequenced]] g; // valid, applies to type specifierhttps://www.open-std.org/jtc1/sc22/wg14/www/docs/n2799.pdf
#if __has_c_attribute(fallthrough)
#warning YES
#else
#warning NO
#endiftry
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2799.pdf
#if __has_include(<stdio.h>)
#warning YES
#endif
#if __has_include(<any.h>)
#warning YES
#else
#warning NO
#endiftry
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2686.pdf
int main()
{
#warning my warning message
}try
Partially implemented.
#include <stdio.h>
int main()
{
static const char file_txt[] = {
#embed "stdio.h"
,0
};
printf("%s\n", file_txt);
}try
try
#define Y
#ifdef X
#define VERSION 1
#elifdef Y
#define VERSION 2
#else
#define VERSION 3
#endifhttps://www.open-std.org/jtc1/sc22/wg14/www/docs/n3033.htm
#define F(...) f(0 __VA_OPT__(,) __VA_ARGS__)
#define G(X, ...) f(0, X __VA_OPT__(,) __VA_ARGS__)
#define SDEF(sname, ...) S sname __VA_OPT__(= { __VA_ARGS__ })
#define EMP
void f(int i, ...) {}
int main()
{
int a = 1;
int b = 2;
int c = 3;
F(a, b, c);
F();
F(EMP);
G(a, b, c);
G(a, );
G(a);
}try
TODO https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2763.pdf
int main()
{
_BitInt(2) a2 = 1;
_BitInt(3) a3 = 2;
_BitInt(33) a33 = 1;
char c = 3;
}try
void F(int *p){}
int main()
{
F((static int []){1, 2, 3, 0});
}try
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3038.htm
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3353.htm
static_assert(0o52 == 052);
static_assert(0O52 == 052);
static_assert(0O52 == 42);
int main()
{
int i = 0o52;
}try
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3370.htm
#include <stdio.h>
void f(int n)
{
switch (n)
{
case 1 ... 10:
printf("n in range 1...10\n");
break;
default:
break;
}
}
int main(){
f(1);
f(11);
}https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3524.txt
//Use -E
#def foo(x)
do {
bar(x);
baz(x);
}
while (0)
#enddef
foo(1)
foo(2)try
It may not be part of C2Y, but it is implemented in Cake while we wait to see whether we will keep it as an extension or remove it.
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3369.pdf
void f(int n)
{
int v[123][n];
static_assert(_Countof(v) == 123);
}
int main()
{
int a[7][3];
int n = _Countof(a);
static_assert(_Countof(a) == 7);
int n2 = _Countof(int [7][3]);
static_assert(_Countof(int [2][3]) == 2);
}try
Obs: Cake extends countof to enums, returning the number of enumerators. (this is not part of C2Y) It can be a new keyword in cake.
#include <string.h>
enum E { A, B, C, D, E, F };
void f(enum E e)
{
switch (e)
{
case A:
break;
case B:
break;
default:
static_assert(_Countof(e) == 6);
}
}
enum E parse_enum_e(const char* s)
{
if (strcmp(s, "A") == 0) return A;
if (strcmp(s, "B") == 0) return B;
if (strcmp(s, "C") == 0) return C;
if (strcmp(s, "D") == 0) return D;
if (strcmp(s, "E") == 0) return E;
if (strcmp(s, "F") == 0) return F;
static_assert(_Countof(enum E) == 6);
return A;
}
int main() { }try
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3734.pdf
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3733.htm
// 12 EXAMPLE 1 Defer statements cannot be jumped over.
#include <stdio.h>
int main()
{
goto target; // constraint violation
_Defer { fputs(" meow", stdout); }
target:
fputs("cat says", stdout);
return 1;
}try
/*g*/
#include <stdio.h>
int main()
{
// print "cat says" to standard output
return fputs("cat says", stdout);
_Defer { fputs(" meow", stdout); } // okay: no constraint violation,
// not executed
}try
/*h*/
#include <stdio.h>
int main()
{
goto target;
{
// okay: no constraint violation
_Defer { fputs(" meow", stdout); }
}
target:
fputs("cat says", stdout);
return 1; // prints "cat says" to standard output
}try
/*i*/
#include <stdio.h>
int main()
{
{
_Defer { fputs("cat says", stdout); }
// okay: no constraint violation
goto target;
}
target:
fputs(" meow", stdout);
return 1; // prints "cat says meow" to standard output
}try
/*j*/
#include <stdio.h>
int main()
{
_Defer {
goto target; // constraint violation
fputs(" meow", stdout);
}
target:
fputs("cat says", stdout);
return 1;
}try
/*k*/
#include <stdio.h>
int main()
{
_Defer {
return 5; // constraint violation
fputs(" meow", stdout);
}
fputs("cat says", stdout);
return 1;
}try
/*l*/
#include <stdio.h>
int main()
{
_Defer
{
target:
fputs(" meow", stdout);
}
goto target; // constraint violation
fputs("cat says", stdout);
return 1;
}try
/*m*/
#include <stdio.h>
int main()
{
goto target; // okay: no constraint violation
{
target:
_Defer { fputs("cat says", stdout); }
}
fputs(" meow", stdout);
return 1; // prints "cat says meow" to standard output
}try
/*n*/
#include <stdio.h>
int main()
{
goto target; // constraint violation!!
{
_Defer {fputs(" meow", stdout); }
target:
}
fputs("cat says", stdout);
return 1;
}try
/*o*/
#include <stdio.h>
int main()
{
{
_Defer fputs("cat says", stdout);
goto target;
}
target:;
fputs(" meow", stdout);
return 1; // prints "cat says meow"
}try
/*p*/
#include <stdio.h>
int main()
{
{
goto target;
_Defer fputs(" meow", stdout);
}
target:;
fputs("cat says", stdout);
return 1; // prints "cat says"
}try
/*q*/
#include <stdio.h>
int main()
{
{
_Defer { fputs(" meow", stdout); }
target:
}
goto target; // constraint violation !!
fputs("cat says", stdout);
return 1;
}try
/*r*/
#include <stdio.h>
int main()
{
{
target:
_Defer { fputs("cat says", stdout); }
}
goto target; // ok
fputs(" meow\n", stdout);
return 1; // prints "cat says" repeatedly
}try
/*s*/
#include <stdio.h>
int main()
{
{
target:
_Defer { fputs("cat says", stdout); }
goto target; // ok
}
// never reached
fputs(" meow", stdout);
return 1; // prints "cat says" repeatedly
}try
/*t*/
#include <stdio.h>
int main()
{
int count = 0;
{
target:
_Defer { fputs("cat says ", stdout); }
++count;
if (count <= 2) {
goto target; // ok
}
}
fputs("meow", stdout);
return 1; // prints "cat says cat says cat says meow"
}try
/*u*/
#include <stdio.h>
int main()
{
int count = 0;
{
_Defer { fputs("cat says", stdout); }
target:
if (count < 5) {
++count;
goto target; // ok
}
}
fputs(" meow", stdout);
return 1; // prints "cat says meow"
}try
/*v*/
#include <stdio.h>
int main()
{
int count = 0;
target:
if (count >= 2) {
fputs("meow", stdout);
return 1; // prints "cat says cat says meow "
}
_Defer { fputs("cat says ", stdout); }
count++;
goto target;
return 0; // never reached
}try
#include <stdio.h>
/*
13 EXAMPLE 2 All the expressions and statements of an
enclosing block are evaluated before executing defer
statements, including any conversions. After all defer
statements are executed, the block is then exited.
*/
int main()
{
int r = 4;
int* p = &r;
_Defer { *p = 5; }
return *p; // return 4;
}try
#include <stddef.h>
#include <stdlib.h>
int use_buffer(size_t n, void* buf)
{
/* ... */
return 0;
}
int main()
{
const int size = 20;
void* buf = malloc(size);
_Defer { free(buf); }
// buffer is not freed until AFTER use_buffer returns
return use_buffer(size, buf);
}try
/*
14 EXAMPLE 3 It is not defined if defer statements execute
their deferred blocks if the exiting / non- returning
functions detailed previously are called.
*/
#include <stdlib.h>
int f()
{
void* p = malloc(1);
if (p == NULL) {
return 0;
}
_Defer free(p);
exit(1); // "p" may be leaked
return 1;
}try
/*
15 EXAMPLE 4 Defer statements, when execution reaches them,
are tied to the scope of the defer statement within their
enclosing block, even if it is a secondary block without
braces.
*/
#include <stdio.h>
#include <stdlib.h>
int main()
{
{
_Defer { fputs(" meow", stdout); }
if (true) _Defer fputs("cat", stdout);
fputs(" says", stdout);
}
// "cat says meow" is printed to standard output
exit(0);
}try
/*
16 This applies to any enclosing block, even for loops
without braces around its body.
*/
#include <stdio.h>
#include <stdlib.h>
int main() {
const char* arr[] = {"cat", "kitty", "ferocious little baby"};
_Defer { fputs(" meow", stdout); }
for (unsigned int i = 0; i < 3; ++i) _Defer printf("my %s,\n", arr[i]);
fputs("says", stdout);
// "my cat,
// my kitty,
// my ferocious little baby,
// says meow"
// is printed to standard output
return 0;
}try
/*
17 EXAMPLE 5 Defer statements execute their deferred blocks
in reverse order of the appearance of the defer statements,
and nested defer statements execute their deferred blocks
in reverse order but at the end of the deferred block they
were invoked within. The following program:
*/
int main() {
int r = 0;
{
_Defer {
_Defer r *= 4;
r *= 2;
_Defer { r += 3; }
}
_Defer r += 1;
}
return r; // return 20;
}try
/*
18 EXAMPLE 6 Defer statements can be executed within a
switch, but a switch cannot be used to jump into the scope
of a defer statement.
*/
#include <stdlib.h>
int main()
{
void* p = malloc(1);
switch (1) {
_Defer free(p); // constraint violation
default:
_Defer free(p);
break;
}
return 0;
}
try
/*
19 EXAMPLE 7 Defer statements can not be exited by means
of break or continue
*/
int main() {
switch (1) {
default:
_Defer {
break; // constraint violation
}
}
for (;;) {
_Defer {
break; // constraint violation
}
}
for (;;) {
_Defer {
continue; // constraint violation
}
}
return 0;
}try
/*
20 EXAMPLE 8 Defer statements that are not reached are
not executed.
*/
#include <stdlib.h>
int main() {
void* p = malloc(1);
return 0;
_Defer free(p); // not executed, p is leaked
}
try
/*
21 EXAMPLE 9 Defer statements can contain other
compound statements.
*/
typedef struct meow* handle;
extern int purr(handle* h);
extern void un_purr(handle h);
int main()
{
handle h;
int err = purr(&h);
_Defer if (!err) un_purr(h);
return 0;
}
try
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3388.htm
#include <stdio.h>
int main()
{
int size = 10;
if (FILE* f = fopen("file.txt", "r"); f)
{
/*...*/
fclose(f);
}
if (FILE* f = fopen("file.txt", "r"))
{
/*...*/
fclose(f);
}
}try
This feature was created in Cake and now it is part of C2Y!
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3260.pdf
int main()
{
const int * const p;
static_assert(_Generic(p, const int *: 1));
/*extension*/
static_assert(_Generic(int, int : 1));
static_assert(_Generic(typeof(p), const int * const: 1));
}try
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3457.htm
/*
Compile with -E
*/
#define X(Z) Z Z
X(__COUNTER__) // 0 0
X(__COUNTER__) // 1 1try
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3678.pdf
int main()
{
static int dup(int a) { return a * 2; }
return dup(1);
}try
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3679.pdf
#include <stdio.h>
int main()
{
printf("%d", (int (void)){
return 1;
}());
}try
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3643.htm
#include <stdio.h>
#define maxint(a,b) \
({int _a = (a), _b = (b); _a > _b ? _a : _b; })
int main()
{
printf("%d", maxint(1, 2));
}try
https://www.open-std.org/JTC1/SC22/WG14/www/docs/n3804.txt
The elvis operator is a shorthand for the ternary operator where the middle operand is omitted. When the condition is true, the condition's own value is returned - evaluated only once.
a ?: b
is equivalent to a ? a : b, except a is evaluated exactly once.
#include <stdio.h>
int main()
{
int x = 0;
int y = 5;
/* returns y because x is 0 (falsy) */
int r1 = x ?: y;
printf("%d\n", r1); /* 5 */
/* returns x because x is non-zero */
x = 3;
int r2 = x ?: y;
printf("%d\n", r2); /* 3 */
}try
The most common use case - return a pointer if non-null, otherwise a default:
#include <stdio.h>
const char *get_name(void);
int main()
{
const char *name = get_name();
const char *display = name ?: "unknown";
printf("%s\n", display);
}try
The elvis operator works in constant expressions:
static_assert(1 ?: 0 == 1);
static_assert(0 ?: 1 == 1);
enum { DEFAULT = 0 ?: 42 };try
When the condition has side effects, it is guaranteed to be evaluated exactly once. Cake introduces a temporary variable in the C89 output to ensure this:
int i = 0;
int b = 10;
int r = i++ ?: b;
/* i is now 1, r is 10 (i++ yielded 0, which is falsy) */C89 output:
int __v0;
__v0 = i++;
int r = __v0 ? __v0 : b;Elvis associates right-to-left like the ternary operator:
int a = 0, b = 0, c = 7;
int r = a ?: b ?: c; /* 7 */try
#define __CAKE__ 202311L
#define __STDC_VERSION__ 202311L
In cake assert is an built-in function. The reason is because it works as tips for flow analysis.
For instance, in a linked list when head is null tail is also null,
and tail->next always points to null.
Assertion will check these properties in runtime and also make the static analysis assume that assert evaluates to true.
void list_push_back(struct list* list,
struct item* _Owner p_item)
{
if (list->head == NULL) {
list->head = p_item;
}
else {
assert(list->tail != nullptr);
assert(list->tail->next == nullptr);
list->tail->next = p_item;
}
list->tail = p_item;
}try
However, assert is not a "blind override command." In situations like:
int i = 0;
assert(i != 0);In situations where static analysis can identify two or more possible states, assert works as a state selector, similar to what happens in if statements but without the scope.
void f(int * _Opt p)
{
if (p != NULL) {
//p is not null here...
}
}
void f2(int * _Opt p)
{
assert(p != NULL);
//we assume p is not null here...
}try
try-statement:
try secondary-block
try secondary-block catch secondary-block
jump-statement:
throw;
try catch is a external block that we can jump off.
try catch is a LOCAL jump this is on purpose not a limitation.
catch block is optional.
extern int error;
int main()
{
try
{
for (int i = 0 ; i < 10; i++)
{
for (int j = 0 ; j < 10; j++)
{
/*...*/
if (error) throw;
/*...*/
}
}
}
catch
{
}
}try
checked-expression:
assignment-expression
assignment-expression !
The checked expression evaluates its operand expression. If the result compares equal to zero, we jump to a catch block. Otherwise, the value of the operand is returned unchanged.
This operator can be applied to any scalar expression, including integers and pointers.
int f();
int* get_ptr();
int main()
{
try {
int i = f()!;
int *p = get_ptr()!;
int a = 1, b = 0;
int x = (a + b)!;
}
catch {
}
}try
#pragma safety enable
void* _Owner _Opt malloc(unsigned long size);
void free(void* _Owner _Opt ptr);
int main() {
try
{
void * _Owner p = malloc(1)!;
free(p);
}
catch{
}
}try
#pragma dir "C:/Program Files (x86)/Windows Kits/10//include/10.0.22000.0/cppwinrt"Add the path to the list of directory paths used to seach include files.
In cake offset (https://en.cppreference.com/w/cpp/types/offsetof.html) is an operator
#include <stdio.h>
struct S
{
char m0;
double m1;
short m2;
char m3;
};
int main()
{
printf("offset of char m0 = %zu", offsetof(struct S, m0));
printf("offset of char m0 = %zu", offsetof(struct S, m1));
printf("offset of char m0 = %zu", offsetof(struct S, m2));
printf("offset of char m0 = %zu", offsetof(struct S, m3));
}try
We have some compile time functions to infer properties of types.
_is_char()
The three types char, signed char, and unsigned char are collectively called the character types.
_is_pointer
Pointer to object or function
_is_array
Array type
_is_function
A function type describes a function with specified return type.
_is_floating_point
float, double, and long double return true
_is_integral
The standard signed integer types and standard unsigned integer types are collectively called the
standard integer types;
_is_arithmetic
Integer and floating types are collectively called arithmetic types.
_is_scalar
Arithmetic types, pointer types, and the nullptr_t type are collectively called scalar typesNote: Type traits that can be easily created with _Generic will be removed. _
See ownership
_Owner
_Opt
_View
- __builtin_va_list
- __builtin_c23_va_start
- __builtin_va_start
- __builtin_va_end
- __builtin_va_arg
- __builtin_va_copy
- __builtin_offsetof (same as cake offsetof)
- __attribute__
- __typeof__ alias same as typeof
Other builtins are declared at \src\include\x86_x64_gcc_builtins.h
Pre-defined macros for GCC compatibility https://gcc.gnu.org/onlinedocs/cpp/Predefined-Macros.html
See \src\include\x86_x64_gcc_builtins.h
- __ptr32, __ptr64
- __int8 ... __int64
- __declspec
- __cdecl
- __fastcall, __stdcall
- __forceinline alias for inline in cake
- __pragma
- __unaligned
Pre-defined macros for MSVC compatibility https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170#standard-predefined-macros
See \src\include\x86_msvc_macros.h and \src\include\x64_msvc_macros.h