├── README.md ├── _config.yml ├── assets ├── above.jpg ├── abyss.jpg ├── below.jpg ├── belowwater.jpg ├── bottom.jpg ├── css │ └── style.scss ├── deep.jpg ├── middle.jpg ├── oniceberg.jpg └── preview.jpg ├── explanations.md └── index.html /README.md: -------------------------------------------------------------------------------- 1 | # PPMP-Iceberg 2 | Preprocessor metaprogramming knowledge obscurity chart. 3 | 4 | Here are (soon) explained various PPMP subjects in order of increasing obscurity. 5 | 6 | https://jadlevesque.github.io/PPMP-Iceberg/ 7 | 8 | 9 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-midnight 2 | markdown: GFM 3 | -------------------------------------------------------------------------------- /assets/above.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JadLevesque/PPMP-Iceberg/6b7d22511d52837777e78a849d9b3d905b14dac1/assets/above.jpg -------------------------------------------------------------------------------- /assets/abyss.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JadLevesque/PPMP-Iceberg/6b7d22511d52837777e78a849d9b3d905b14dac1/assets/abyss.jpg -------------------------------------------------------------------------------- /assets/below.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JadLevesque/PPMP-Iceberg/6b7d22511d52837777e78a849d9b3d905b14dac1/assets/below.jpg -------------------------------------------------------------------------------- /assets/belowwater.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JadLevesque/PPMP-Iceberg/6b7d22511d52837777e78a849d9b3d905b14dac1/assets/belowwater.jpg -------------------------------------------------------------------------------- /assets/bottom.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JadLevesque/PPMP-Iceberg/6b7d22511d52837777e78a849d9b3d905b14dac1/assets/bottom.jpg -------------------------------------------------------------------------------- /assets/css/style.scss: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | @import "{{ site.theme }}"; 5 | 6 | #header { 7 | top: inherit; 8 | bottom: 0px; 9 | } 10 | 11 | h1 { 12 | font-size: 32px; 13 | text-align: center; 14 | margin-top: 2em; 15 | } 16 | 17 | #title h1 { 18 | margin-top: inherit; 19 | } 20 | 21 | h2 { 22 | margin-top: 2em; 23 | font-size: 22px; 24 | } 25 | h2 code { 26 | font-size: inherit; 27 | color: inherit; 28 | } 29 | h2:before { content:"➤ "; } 30 | -------------------------------------------------------------------------------- /assets/deep.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JadLevesque/PPMP-Iceberg/6b7d22511d52837777e78a849d9b3d905b14dac1/assets/deep.jpg -------------------------------------------------------------------------------- /assets/middle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JadLevesque/PPMP-Iceberg/6b7d22511d52837777e78a849d9b3d905b14dac1/assets/middle.jpg -------------------------------------------------------------------------------- /assets/oniceberg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JadLevesque/PPMP-Iceberg/6b7d22511d52837777e78a849d9b3d905b14dac1/assets/oniceberg.jpg -------------------------------------------------------------------------------- /assets/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JadLevesque/PPMP-Iceberg/6b7d22511d52837777e78a849d9b3d905b14dac1/assets/preview.jpg -------------------------------------------------------------------------------- /explanations.md: -------------------------------------------------------------------------------- 1 | # Above the iceberg 2 | 3 | ## [`#include`](https://en.cppreference.com/w/c/preprocessor/include) 4 | 5 | ## macros just replace text 6 | 7 | Macros just replace text, they don't know anything about the surrounding C code. 8 | 9 | ## [header guards](https://en.wikipedia.org/wiki/Include_guard) 10 | 11 | ## [`#if #elif`](https://en.wikipedia.org/wiki/C_preprocessor#Conditional_compilation) 12 | 13 | 14 | ## make sure to parenthesize arguments 15 | 16 | Often macros are used for code generation purposes, take for example: 17 | ```c 18 | #define ADD(a, b) a += b 19 | ``` 20 | This will have unexpected behavior in many circumstances: 21 | ```c 22 | ADD(x, y; z); 23 | w = (2 + ADD(x, y) + z); 24 | w = (ADD(x, y) + z); 25 | ``` 26 | To solve these problems make sure to parenthesize arguments, and the complete expression: 27 | ```c 28 | #define ADD(a, b) ((a) += (b)) 29 | ``` 30 | 31 | ## [`#pragma once`](https://en.wikipedia.org/wiki/Pragma_once) (extension) 32 | 33 | ## `do { } while (0)` 34 | 35 | When writing a more complex code generation macro that isn't a single expression, then you want it to at least fit into a single statement, so it behaves like other language elements. 36 | 37 | The naïve implementation doesn't have such properties: 38 | 39 | ```c 40 | #define FOR_EACH(f,a) \ 41 | int i; \ 42 | for (i = 0; i < sizeof (a) / sizeof *(a); i++) { \ 43 | (f) ((a)[i]); \ 44 | } 45 | // what about this case? 46 | for (int i = 0; i < 10; ++i) 47 | FOR_EACH(f,a[i]); 48 | ``` 49 | 50 | A somewhat better solution is to enclose the code in a compound-statement: 51 | 52 | ```c 53 | #define FOR_EACH(f,a) \ 54 | { \ 55 | int i; \ 56 | for (i = 0; i < sizeof (a) / sizeof *(a); i++) { \ 57 | (f) ((a)[i]); \ 58 | } \ 59 | } 60 | // this works now 61 | for (int i = 0; i < 10; ++i) 62 | FOR_EACH(f,a[i]); 63 | // ^ 64 | // but this doesn't 65 | if (cnd) 66 | FOR_EACH(f,A); 67 | else 68 | ... 69 | ``` 70 | This does work in more cases, but not in every case. The canonical way to fix this is to use a `do {} while (0)` block: 71 | ```c 72 | #define FOR_EACH(f,a) \ 73 | do { \ 74 | int i; \ 75 | for (i = 0; i < sizeof (a) / sizeof *(a); i++) { \ 76 | (f) ((a)[i]); \ 77 | } \ 78 | } while (0) 79 | // this still works 80 | for (int i = 0; i < 10; ++i) 81 | FOR_EACH(f,a[i]); 82 | // ^ 83 | // this works now! 84 | if (cnd) 85 | FOR_EACH(f,A); 86 | else 87 | ... 88 | 89 | ``` 90 | 91 | ## [`ARRAY_LEN()`](https://www.ashn.dev/blog/2020-01-06-c-array-length.html) 92 | 93 | 94 | 95 | # On the iceberg 96 | 97 | ## macro expansion isn't recursive 98 | 99 | ```c 100 | #define A(x) A(x x) 101 | A(x) // A(x x) 102 | 103 | #define B(x) C(x x) 104 | #define C(x) B(x x) 105 | B(x) // B(x x x x) 106 | ``` 107 | 108 | ## `#a` 109 | 110 | ```c 111 | #define STR(a) #a 112 | STR(123 foo bar) // "123 foo bar" 113 | ``` 114 | 115 | ## `a##b` 116 | 117 | ```c 118 | #define CAT(a,b) a##b 119 | #define FOOBAR ~ 120 | CAT(FOO,BAR) // ~ 121 | ``` 122 | 123 | ## [`__VA_ARGS__`](https://stackoverflow.com/questions/26053959/what-does-va-args-in-a-macro-mean) 124 | 125 | ## [`#undef`](https://en.cppreference.com/w/c/preprocessor/replace#mw-headline) 126 | 127 | ## `#error` 128 | Produces a custom error recorded on the buildlog as well as halting compilation. 129 | 130 | ```c 131 | #error "You did something wrong at line something." 132 | ``` 133 | 134 | ## [`defined`](https://en.cppreference.com/w/c/preprocessor/conditional#Combined_directives) 135 | 136 | ## [physical vs logical source lines](https://en.cppreference.com/w/c/language/translation_phases#Phase_2) 137 | 138 | ## [X macros](https://en.wikibooks.org/wiki/C_Programming/Preprocessor_directives_and_macros#X-Macros=) 139 | 140 | 141 | 142 | # Below the water 143 | 144 | ## `__FILE__` `__LINE__` 145 | `__FILE__` expands to a string literal containing the name of the current file. 146 | `__LINE__` expands to an integer literal of the value of the line where it is expanded. 147 | 148 | ## `__DATE__` `__TIME__` 149 | `__DATE__` expands to a string literal containing the date of compilation. 150 | `__TIME__` expands to a string literal containing the time of compilation. 151 | 152 | ## [Trigraphs](https://en.cppreference.com/w/c/language/operator_alternative#Trigraphs) 153 | 154 | ## `#line` 155 | Sets a new value for `__FILE__` and `__LINE__`. 156 | 157 | ```c 158 | #line 42 "I/am/the/capitain.now" 159 | __LINE__:__FILE__ // 42:"I/am/the/capitain.now" 160 | ``` 161 | 162 | ## function like macros only see parentheses 163 | 164 | Function like macros only see parentheses when it comes to splitting up the arguments, e.g. `FOO({1,3})` calls `FOO` with the arguments `{1` and `3}`. 165 | 166 | This problem often occurs when passing a compound literal, e.g. `(struct Vec3){1,2,3}`, to a function like macro. 167 | 168 | To circumvent this, always pass compound literal enclosed in parentheses. 169 | 170 | ## [`__VA_OPT__`](https://en.cppreference.com/w/cpp/preprocessor/replace#Function-like_macros) (C++20 and extension) 171 | 172 | ## [`#define A(x...)`](https://gcc.gnu.org/onlinedocs/gcc/Variadic-Macros.html) (extension) 173 | 174 | ## [`,##__VA_ARGS__`](https://gcc.gnu.org/onlinedocs/gcc/Variadic-Macros.html) (extension) 175 | 176 | ## [`#pragma _Pragma()`](https://port70.net/~nsz/c/c11/n1570.html#6.10.9) 177 | 178 | ## `-P -E` (not standardized) 179 | 180 | Many compilers (gcc,clang,tcc,...) have the options `-E` for only running the preprocessor and `-P` for not omitting line marks. 181 | 182 | ## `#if static_cast(-1)` 183 | 184 | The `#if` statement replaces, after macro expansion, every remaining identifier with the pp-number 0. 185 | So `#if static_cast(-1)` is equivalent to `#if 0<0>(-1)`, `#if 0 > -1`, and `#if 1`. 186 | 187 | ## preprocessing directives can't be inside of macros 188 | 189 | > If there are sequences of preprocessing tokens within the list of arguments that would otherwise act as preprocessing directives, the behavior is undefined. 190 | 191 | (https://port70.net/~nsz/c/c11/n1570.html#6.10.3p11) 192 | 193 | > Each # preprocessing token in the replacement list for a function-like macro shall be followed by a parameter as the next preprocessing token in the replacement list. 194 | 195 | (https://port70.net/~nsz/c/c11/n1570.html#6.10.3.2p1) 196 | 197 | Hence, it's not possible to generate preprocessor directives using standard macros. 198 | 199 | (The rules are similar in C++) 200 | 201 | 202 | 203 | # Middle of the iceberg 204 | 205 | ## `__COUNTER__` (extension) 206 | 207 | Most of widespread compilers (clang, gcc, msvc, icc, lcc, tinyc, chibic and some other) offer the language extension `__COUNTER__`, expanding to an integer value starting at `0` and incrementing the value after every expansion: 208 | ```c 209 | __COUNTER__ // 0 210 | __COUNTER__ // 1 211 | __COUNTER__ // 2 212 | ``` 213 | 214 | 215 | ## prefix namespaces 216 | 217 | Because macro definitions are global there is no builtin namespace facility, it's recommended for libraries to prefix all the macros they define with a characteristic prefix, e.g.: `LIBRARYNAME_` 218 | 219 | ## stringify macros 220 | 221 | ```c 222 | #define STR(a) STR_(a) 223 | #define STR_(a) #a 224 | 225 | #define AWOO ~ 226 | 227 | STR_(AWOO) // "AWOO" 228 | STR(AWOO) // "~" 229 | ``` 230 | 231 | ## `#define CAT(a,b) CAT_(a,b) #define CAT_(a,b) a##b` 232 | 233 | ```c 234 | #define CAT(a,b) CAT_(a,b) 235 | #define CAT_(a,b) a##b 236 | 237 | #define foo FOO 238 | #define bar BAR 239 | #define FOOBAR ~ 240 | 241 | CAT_(foo,bar) // foobar 242 | CAT(foo,bar) // ~ 243 | ``` 244 | 245 | ## [`__has_include`](https://en.cppreference.com/w/cpp/preprocessor/include) (C++17) 246 | 247 | ## [`#elifdef #elifndef`](http://www2.open-std.org/JTC1/SC22/WG14/www/docs/n2645.pdf) (C2x) 248 | 249 | ## [`#embed`](http://www2.open-std.org/JTC1/SC22/WG14/www/docs/n2967.htm) (C2x proposal) 250 | 251 | ## `SCAN()` 252 | 253 | The `SCAN` macro can be used to scan its arguments twice: 254 | 255 | ```c 256 | #define SCAN(...) __VA_ARGS__ 257 | #define SCAN2(...) SCAN(__VA_ARGS__) 258 | #define STR(x) #x 259 | #define A(x) (x+x) 260 | #define B(x) (x+x) 261 | 262 | STR A(1) // STR (x+x) 263 | SCAN(STR A(1)) // "(x+x)" 264 | SCAN(STR B A(1)) // STR (x+x+x+x) 265 | SCAN2(STR B A(1)) // "(x+x+x+x)" 266 | ``` 267 | 268 | ``` 269 | 1. STR A(1) 270 | ^ no arguments supplied, so it's ignored 271 | 2. STR A(1) 272 | ^ expands 273 | Isolated expansion of A's arguments to (1+1) 274 | 3. STR (1+1) 275 | Done 276 | ``` 277 | 278 | ``` 279 | 1. SCAN(STR A(1)) 280 | ^ expands 281 | Isolated expansion of SCAN's arguments: 282 | 2. STR A(1) 283 | ^ no arguments supplied, so it's ignored 284 | 3. STR A(1) 285 | ^ expands 286 | Isolated expansion of A's arguments to (1+1) 287 | Expansion of SCAN finished, resulting tokens are rescanned 288 | 5. STR (1+1) 289 | ^ expands 290 | Isolated expansion of STR's arguments to "1+1" 291 | 6. "(1+1)" 292 | Done 293 | ``` 294 | 295 | ## no more than 4095 296 | 297 | The [C99 translation limits](https://port70.net/~nsz/c/c99/n1256.html#5.2.4.1) only require an implementation to support 4095 simultaneously defined macros, and crucially only requires to support 4095 characters in a logical source line. 298 | 299 | Macros can only be defined in a single logical source line, that sets the limit on the macro replacement list length, for portable programs, to `4085` characters (assuming you use `#define A ...`). 300 | 301 | The same is true for [C11](https://port70.net/~nsz/c/c11/n1570.html#5.2.4.1) and [C2x](https://port70.net/~nsz/c/c2x/n2434.pdf#subsubsection.5.2.4.1). In [C89](https://port70.net/~nsz/c/c89/c89-draft.html#2.2.4.1) the limits are 1024 simultaneously defined macros and 509 characters in a logical source line. 302 | 303 | For C++ both minimal limits are defined as 65536. 304 | 305 | ## [mcpp](https://netcologne.dl.sourceforge.net/project/mcpp/mcpp/V.2.7.2/mcpp-summary-272.pdf) 306 | 307 | ## overloading macros based on argument count 308 | 309 | ```c 310 | #define GET_MACRO(_1,_2,_3,x,...) x 311 | #define FOO(...) GET_MACRO(__VA_ARGS__,FOO3,FOO2,FOO1)(__VA_ARGS__) 312 | 313 | FOO(1) // FOO1(1) 314 | FOO(1,2) // FOO2(1,2) 315 | FOO(1,2,3) // FOO3(1,2,3) 316 | ``` 317 | 318 | ## default arguments 319 | 320 | By [overloading macros based on argument count](#overloading-macros-based-on-argument-count) it's possible to implement default arguments for functions: 321 | 322 | ```c 323 | void foo(int a, int b, float c); 324 | #define GET_ARGS(_1,_2,_3,x,...) x 325 | #define foo(...) GET_ARGS(__VA_ARGS__, \ 326 | foo(__VA_ARGS__), \ 327 | foo(__VA_ARGS__,3), \ 328 | foo(__VA_ARGS__,2,3),) 329 | foo(1,2,3) // foo(1,2,3) 330 | foo(1,2) // foo(1,2,3) 331 | foo(1) // foo(1,2,3) 332 | ``` 333 | 334 | 335 | 336 | # Bottom of the iceberg 337 | 338 | ## [Microsoft took 30 years to implement a standard compliant preprocessor](https://docs.microsoft.com/en-us/cpp/preprocessor/preprocessor-experimental-overview?view=msvc-170) 339 | 340 | ## no argument means one argument 341 | 342 | ```c 343 | #define NO_ARGUMENT() 344 | #define ONE_ARGUMENT(x) x 345 | 346 | NO_ARGUMENT() 347 | // NO_ARGUMENT(1) // error 348 | ONE_ARGUMENT(1) 349 | ONE_ARGUMENT() // this also works 350 | ``` 351 | 352 | ## [blue paint](https://en.wikipedia.org/wiki/Painted_blue) 353 | 354 | ## `CHECK()` 355 | 356 | The `CHECK` macro can be used to detect the existence or non-existence of a probe: 357 | 358 | ```c 359 | #define TUPLE_AT_1(b,a,...) a 360 | #define CHECK(...) TUPLE_AT_1(__VA_ARGS__,) 361 | #define PROBE ,found 362 | 363 | CHECK(PROBE,not found) // found 364 | CHECK(NOT_PROBE,not found) // not found 365 | ``` 366 | 367 | Crucially, this can be used to convert a token to a boolean (0 -> 0, not 0 -> 1): 368 | 369 | ```c 370 | #define BOOL_0_0 ,0 371 | #define BOOL(x) CHECK(BOOL_0_##x,1) 372 | BOOL(0) // 0 373 | BOOL(1) // 1 374 | BOOL(abc) // 1 375 | ``` 376 | 377 | ## saturated overloading 378 | 379 | ```c 380 | #define TUPLE_AT_1(x0,x1,...) x1 381 | 382 | #define COMMA_N(x) ,x 383 | #define FOO(...) FOO_I(__VA_ARGS__,COMMA_N(FOO3),COMMA_N(FOO2),COMMA_N(FOO1),)(__VA_ARGS__) 384 | #define FOO_I(x0,x1,x2,o,...) TUPLE_AT_1(o,FOOn,) 385 | 386 | FOO(1) // FOO1(1,) 387 | FOO(1,2) // FOO2(1,2) 388 | FOO(1,2,3) // FOO3(1,2,3) 389 | FOO(1,2,3,4) // FOOn(1,2,3,4) 390 | FOO(1,2,3,4,5) // FOOn(1,2,3,4,5) 391 | ``` 392 | 393 | ## `INC()/DEC()` 394 | 395 | Many libraries use a structure similar to the following to implement counting in the preprocessor: 396 | 397 | ```c 398 | #define AT_0(a,b,c,d,e,f,g,h) a 399 | #define AT_1(a,b,c,d,e,f,g,h) b 400 | #define AT_2(a,b,c,d,e,f,g,h) c 401 | #define AT_3(a,b,c,d,e,f,g,h) d 402 | #define AT_4(a,b,c,d,e,f,g,h) e 403 | #define AT_5(a,b,c,d,e,f,g,h) f 404 | #define AT_6(a,b,c,d,e,f,g,h) g 405 | #define AT_7(a,b,c,d,e,f,g,h) h 406 | 407 | #define INC_(n) AT_##n(1,2,3,4,5,6,7,0) 408 | #define DEC_(n) AT_##n(7,0,1,2,3,4,5,6) 409 | #define INC(n) INC_(n) 410 | #define DEC(n) DEC_(n) 411 | 412 | INC(6) // 7 413 | INC(DEC(INC(INC(1)))) // 3 414 | ``` 415 | 416 | This approach is quite limited, see [#integer-arithmetics](#integer-arithmetics) for more advanced math. 417 | 418 | 419 | ## `EVAL()/DEFER()` 420 | 421 | It is possible to defer a otherwise recursive macro expansion to avoid it getting [painted blue](#painted-blue). Thus, rescanning a defered macro causes it to expand: 422 | 423 | ```c 424 | #define SCAN(...) __VA_ARGS__ 425 | #define EMPTY() 426 | #define LOOP_INDIRECTION() LOOP 427 | #define LOOP(x) x LOOP_INDIRECTION EMPTY()() (x) 428 | LOOP(1) // 1 LOOP_INDIRECTION () (1) 429 | SCAN(LOOP(1)) // 1 1 LOOP_INDIRECTION () (1) 430 | ``` 431 | 432 | ``` 433 | 1. SCAN(LOOP(1)) 434 | Isolated expansion of SCAN's arguments: 435 | 2. LOOP(1) 436 | ^ expands 437 | Isolated expansion of LOOP's argument to 1 438 | Expansion of LOOP finished, resulting tokens are rescanned 439 | 3. 1 LOOP_INDIRECTION EMPTY()() (1) 440 | ^ pp-number ignored 441 | 4. 1 LOOP_INDIRECTION EMPTY()() (1) 442 | ^ function like macro without arguments, ignored (crucially, not painted blue) 443 | 5. 1 LOOP_INDIRECTION EMPTY()() (1) 444 | ^ expands 445 | 6. 1 LOOP_INDIRECTION () (1) 446 | ^^^^^^ punctuators, ignored 447 | Expansion of SCAN finished, resulting tokens are rescanned 448 | 7. 1 LOOP_INDIRECTION () (1) 449 | ^ pp-num ignored 450 | 8. 1 LOOP_INDIRECTION () (1) 451 | ^ expands 452 | 9. 1 LOOP (1) 453 | ^ expands 454 | ... (see 2. to 6.) 455 | 14.: 1 1 LOOP_INDIRECTION () (1) 456 | ``` 457 | 458 | The insertion of the `EMPTY` macro is sometimes abbreviated to `DEFER(LOOP_INDIRECTION)(x)` using `#define DEFER(id) id EMPTY()`, although it isn't much shorter, and just adds an unnecessary macro expansion. 459 | 460 | There is no reason to stop at a single rescan. Using nested `SCAN` macros, in this context often called `EVAL`, one can exponentially increase the rescan count by adding another line of code.: 461 | 462 | ```c 463 | #define EVAL3(...) EVAL2(EVAL2(EVAL2(EVAL2(__VA_ARGS__)))) 464 | #define EVAL2(...) EVAL1(EVAL1(EVAL1(EVAL1(__VA_ARGS__)))) 465 | #define EVAL1(...) __VA_ARGS__ 466 | EVAL3(LOOP(1)) 467 | // 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 LOOP_INDIRECTION () (1) 468 | ``` 469 | By stopping the fake recursion once your algorithm is complete, this technique can be used in very powerful ways: 470 | 471 | ```c 472 | #define E3(...) E2(E2(E2(E2(E2(E2(E2(E2(E2(E2(E2(E2(E2(__VA_ARGS__))))))))))))) 473 | #define E2(...) E1(E1(E1(E1(E1(E1(E1(E1(E1(E1(E1(E1(E1(__VA_ARGS__))))))))))))) 474 | #define E1(...) __VA_ARGS__ 475 | 476 | #define EMPTY() 477 | #define TUPLE_AT_1(x,y,...) y 478 | #define CHECK(x,...) TUPLE_AT_1(__VA_ARGS__,x,) 479 | 480 | #define LOOP_END_END ,LOOP1 481 | #define LOOP(f,x,...) CHECK(LOOP0,LOOP_END_##x)(f,x,__VA_ARGS__) 482 | #define LOOP_INDIRECTION() LOOP 483 | #define LOOP0(f,x,...) f(x) LOOP_INDIRECTION EMPTY()() (f,__VA_ARGS__) 484 | #define LOOP1(...) 485 | 486 | E3(LOOP(f,1,2,3,4,5,6,7,8,9,END)) 487 | // f(1) f(2) f(3) f(4) f(5) f(6) f(7) f(8) f(9) 488 | ``` 489 | 490 | Note that rescanning even once no more macros are defered still takes some time, so you can't just create a macro that rescans e.g. 2^64 times without there being a rather large constant overhead for every use of that function. For a method of circumventing this, see [the continuation machine 491 | ](#continuation-machine) 492 | 493 | ## [Boost preprocessor](https://www.boost.org/doc/libs/1_75_0/libs/preprocessor/doc/index.html) 494 | 495 | 496 | 497 | # Below the iceberg 498 | 499 | ## list datastructure 500 | 501 | ```c 502 | #define LIST_HEAD(a,b) a 503 | #define LIST_TAIL(a,b) b 504 | 505 | LIST_HEAD(1,(2,(3,))) // 1 506 | LIST_TAIL(1,(2,(3,))) // (2,(3,)) 507 | 508 | #define TUPLE_AT_1(x,y,...) y 509 | #define CHECK(...) TUPLE_AT_1(__VA_ARGS__,) 510 | #define LIST_END(...) ,0 511 | #define LIST_IS_END(x) CHECK(LIST_END x,1) 512 | 513 | LIST_IS_END((9,)) // 0 514 | LIST_IS_END(LIST_TAIL(9,)) // 1 515 | ``` 516 | 517 | ## `#2""3` (extension) 518 | 519 | The first five characters of a preprocessor prototyping test file, that remove most warnings and shortens the current file name to nothing. 520 | Under GCC, this is called a line marker. This one sets the line under it as `2` and the file name as nothing (some systems will 521 | transform this as `""` on the buildlog). The `3` tells the compiler to treat the current file as a system header. 522 | 523 | > Syntax: `#` <*line number*> <*file name*> <*flag*> 524 | 525 | ```c 526 | #2""3 527 | 528 | #warning "Not sneaky" // appears on the buildlog 529 | #pragma GCC warning "Very sneaky" // doesn't appear on the buildlog 530 | #error "Not a warning" // appears on the buildlog 531 | ``` 532 | 533 | gcc buildlog with `#2""3`: 534 | ``` 535 | :3:2: warning: #warning "Not sneaky" [-Wcpp] 536 | :5:2: error: #error "Not a warning" 537 | ``` 538 | 539 | gcc buildlog without `#2""3`: 540 | ``` 541 | :3:2: warning: #warning "Not sneaky" [-Wcpp] 542 | 3 | #warning "Not sneaky" // appears on the buildlog 543 | | ^~~~~~~ 544 | :4:21: warning: Very sneaky 545 | 4 | #pragma GCC warning "Very sneaky" // doesn't appear on the buildlog 546 | | ^~~~~~~~~~~~~ 547 | :5:2: error: #error "Not a warning" 548 | 5 | #error "Not a warning" // appears on the buildlog 549 | | ^~~~~ 550 | ``` 551 | 552 | ### lazy `P` argument 553 | 554 | Passing an empty argument to a macro (usually the first argument called `P` for historical reasons) allows it to stop the expansion of arguments, before the tokes are rescanned: 555 | 556 | 557 | ```c 558 | #define OPEN(...) __VA_ARGS__ 559 | #define OPENq(P,...) P##__VA_ARGS__ 560 | 561 | #define A() ~ 562 | #define NOTHING 563 | 564 | OPEN(A NOTHING ()) // ~ 565 | OPENq(,A NOTHING ()) // A () 566 | ``` 567 | 568 | This can also be used to increase performance, by delaying a macro expansion, when the expansion results in more tokens than the macro call. 569 | 570 | ```c 571 | #define R2(x) R(R(R(R(R(R(R(R(R(R(x)))))))))) 572 | #define R(x) x,x,x,x,x,x 573 | 574 | #define EAT(...) EAT_(__VA_ARGS__) 575 | #define EAT_(...) 576 | 577 | EAT(OPEN(R2(a))) // 4.2 seconds 578 | EAT(OPENq(,R2(a))) // 3.1 seconds 579 | ``` 580 | 581 | The above code might need too much memory in gcc and clang, it was tested with tcc. 582 | 583 | 584 | ## C89 `CHECK()` 585 | 586 | ```c 587 | #define CHECK_EAT(x,y) 588 | #define CHECK_RESULT(x) CHECK_RESULT_ x 589 | #define CHECK_RESULT_(x,y) y 590 | #define CHECK(P,x,y) CHECK_RESULT((P##x,y)) 591 | 592 | #define PROBE ,found)CHECK_EAT( 593 | 594 | CHECK(,PROBE,not found) // found 595 | CHECK(,NOT_PROBE,not found) // not found 596 | ``` 597 | 598 | ## pp-num prefix 599 | 600 | When a library uses identifiers that will later be concatenated with a macro, it's advisory for them to prefix these with a pp-number. 601 | E.g. [order-pp](#order-pp) uses this extensively: 602 | 603 | ```c 604 | ORDER_PP 605 | (8let ((8X, 8nat (1,2,3)) 606 | (8Y, 8nat (4,5,6)) 607 | ,8to_lit (8mul (8X, 8Y)) 608 | ) 609 | ) 610 | ``` 611 | 612 | If the above code wouldn't use pp-num prefixes, it would break when surrounding code, e.g. uses `#define mul ...`. 613 | 614 | 615 | ## lazy arguments without `P` (extension) 616 | 617 | Comma concatenation is a GCC extension. 618 | 619 | ```c 620 | #define LEFT(P,...) P##__VA_ARGS__ 621 | #define LAZY_WITHOUT_P(...) LEFT(,##__VA_ARGS__) 622 | #define NOTHING 623 | 624 | #define A() ~ 625 | 626 | LAZY_WITHOUT_P(A ()) // ~ 627 | LAZY_WITHOUT_P(A NOTHING ()) // A () 628 | ``` 629 | 630 | ## [`#include_next`](https://gcc.gnu.org/onlinedocs/cpp/Wrapper-Headers.html) (extension) 631 | 632 | ## [`#assert`](https://gcc.gnu.org/onlinedocs/gcc-4.3.1/cpp/Assertions.html) (extension) 633 | 634 | ## [`IS_EMPTY()`](https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/) 635 | 636 | ## [ppstep](https://github.com/notfoundry/ppstep) 637 | 638 | ## sequence datastructure 639 | 640 | A sequence is a group of adjacent parenthesized elements, e.g. `(1)(2)()((),w,())(awoo())(())`: 641 | 642 | * insert front: fast (`(x)seq`) 643 | * insert back: fast (`seq(x)`) 644 | * pop front: fast (`EAT seq`) 645 | * back front: slow 646 | * iteration: fast ([see `#A(1)(2)(3)(4)(5)`](#a12345)) 647 | 648 | ## [OBJECT disabling context](https://marc.info/?l=boost&m=118835769257658) 649 | 650 | 651 | # Deep water 652 | 653 | 654 | ## `A(1)(2)(3)(4)(5)` 655 | 656 | Iterating over a sequence is quite easy: 657 | 658 | ```c 659 | #define A(x) f(x) B 660 | #define B(x) f(x) A 661 | 662 | A(1)(2)(3)(4)(5) // f(1) f(2) f(3) f(4) f(5) B 663 | ``` 664 | 665 | Sadly it's implementation defined behavior whether this works in C. 666 | 667 | The relevant standard passage is [Annex J.1](https://port70.net/~nsz/c/c11/n1570.html#J.1): 668 | 669 | > When a fully expanded macro replacement list contains a function-like macro name as its last preprocessing token and the next preprocessing token from the source file is a (, and the fully expanded replacement of that macro ends with the name of the first macro and the next preprocessing token from the source file is again a (, whether that is considered a nested replacement 670 | 671 | As well as the example from [6.10.3.4p4](https://port70.net/~nsz/c/c11/n1570.html#6.10.3.4p4): 672 | 673 | > EXAMPLE There are cases where it is not clear whether a replacement is nested or not. For example, given the following macro definitions: 674 | > ```c 675 | > #define f(a) a*g 676 | > #define g(a) f(a) 677 | > ``` 678 | > the invocation `f(2)(9)` may expand to either `2*f(9)` or `2*9*g`. 679 | > Strictly conforming programs are not permitted to depend on such unspecified behavior. 680 | 681 | Fortunately for us, almost all preprocessor allow for sequence iteration, and crucially it is possible to detect whether your preprocessor supports: 682 | 683 | ```c 684 | #define CAT(a,b) CAT_(a,b) 685 | #define CAT_(a,b) a##b 686 | 687 | #define HAS_SEQ_ITER CAT(X_,HAS_SEQ_ITERa()()()) 688 | #define HAS_SEQ_ITERa() HAS_SEQ_ITERb 689 | #define HAS_SEQ_ITERb() HAS_SEQ_ITERa 690 | 691 | #define X_HAS_SEQ_ITERa() 0 692 | #define X_HAS_SEQ_ITERb 1 693 | 694 | #if HAS_SEQ_ITER 695 | // use sequence iteration 696 | #else 697 | // don't use sequence iteration 698 | #endif 699 | ``` 700 | 701 | ### Terminating sequence iteration / removing the trailing 702 | 703 | After the sequence iteration, you still need to get rid of the leftover function like macro without arguments. 704 | The easiest method probably is the following: 705 | 706 | ```c 707 | #define SEQ_TERM(...) SEQ_TERM_(__VA_ARGS__) 708 | #define SEQ_TERM_(...) __VA_ARGS__##_RM 709 | 710 | #define A(x) f(x),B 711 | #define B(x) f(x),A 712 | 713 | #define A_RM 714 | #define B_RM 715 | 716 | SEQ_TERM(A(1)(2)(3)(4)(5)) // f(1),f(2),f(3),f(4),f(5), 717 | ``` 718 | 719 | 720 | 721 | ## Order-pp 722 | 723 | [Order-pp](https://github.com/rofl0r/order-pp) 724 | 725 | ```c 726 | #include 727 | #include 728 | 729 | #include 730 | 731 | 732 | #ifndef ORDER_PP_DEF_8singleton 733 | #define ORDER_PP_DEF_8singleton ORDER_PP_FN_CM(1,8SINGLETON,0IS_ANY) 734 | #define ORDER_PP_8SINGLETON(P,x,...) (,(P##x),P##__VA_ARGS__) 735 | #endif 736 | 737 | #define TOTAL_STRLEN(...) \ 738 | ORDER_PP \ 739 | (8lets ((8S, 8tuple_to_seq (8quote ((__VA_ARGS__)))) \ 740 | (8M, 8seq_map (8compose (8adjacent (8quote (+strlen)) \ 741 | ,8singleton \ 742 | ) \ 743 | ,8S \ 744 | ) \ 745 | ) \ 746 | ,8seq_fold (8adjacent \ 747 | ,0 \ 748 | ,8M \ 749 | ) \ 750 | ) \ 751 | ) 752 | 753 | 754 | int main () { 755 | printf ("%i\n" 756 | ,TOTAL_STRLEN ("123", "34634523", ((char[]){'5', '2', '1', '\0'})) 757 | // 0 +strlen("123")+strlen("34634523")+strlen(((char[]){'5', '2', '1', '\0'})) 758 | ); 759 | // output: 14 760 | 761 | return 0; 762 | } 763 | ``` 764 | 765 | ## [chaos-pp](https://github.com/rofl0r/chaos-pp) 766 | 767 | ## [Metalang99](https://github.com/Hirrolot/metalang99) 768 | 769 | ## [Continuation machine](https://github.com/camel-cdr/bfcpp/blob/main/TUTORIAL.md#user-content-141-the-continuation-machine=) 770 | 771 | ## macro stack 772 | 773 | Extension on multiple preprocessors (GCC, CLang, MSVC,...) 774 | 775 | Macro definitions can be stacked using the `push_macro` and `pop_macro` pragmas. This is very useful for cross-MTU (macro translation unit) memory. 776 | 777 | ```c 778 | #2""3 779 | #define PRAMGA(...) _Pragma(#__VA_ARGS__) 780 | #define POP(m) PRAMGA(pop_macro(#m)) 781 | 782 | #define X 1 POP(X) 783 | #pragma push_macro("X") 784 | #define X 2 POP(X) 785 | #pragma push_macro("X") 786 | #define X 3 POP(X) 787 | #pragma push_macro("X") 788 | #define X 4 POP(X) 789 | X 790 | X 791 | X 792 | X 793 | ``` 794 | Output: 795 | ```c 796 | 4 797 | 798 | 3 799 | 800 | 2 801 | 802 | 1 803 | ``` 804 | 805 | ## interpreters/compilers 806 | 807 | * [bfcpp](https://github.com/camel-cdr/bfcpp) (Optimizing Brainfuck interpreter) 808 | * [bfi](http://www.kotha.net/bfi/) (Optimizing Brainfuck interpreter) 809 | * [CPP_COMPLETE](https://github.com/orangeduck/CPP_COMPLETE) (Brainfuck interpreter) 810 | * [ppasm](https://github.com/notfoundry/ppasm) (x86_64 macro assembler) 811 | 812 | ## language extensions 813 | 814 | * [datatype99](https://github.com/Hirrolot/datatype99) (Algebraic data types) 815 | * [interface99](https://github.com/Hirrolot/interface99) (Interfaces) 816 | 817 | 818 | ## binary search `SCAN` 819 | 820 | You can't nest the `SCAN` macro by default: 821 | 822 | ```c 823 | #define SCAN0(...) __VA_ARGS__ 824 | #define SCAN1(...) __VA_ARGS__ 825 | #define SCAN2(...) __VA_ARGS__ 826 | #define SCAN3(...) __VA_ARGS__ 827 | 828 | #define EMPTY() 829 | 830 | #define A_() A 831 | #define A(x) x A_ EMPTY() ()(x) 832 | 833 | #define A2_() A2 834 | #define A2(x) SCAN0(A(1)) A2_ EMPTY() ()(x) 835 | // doesn't work ---v 836 | SCAN0(A2(2)) // 1 1 1 A_ ()(1) SCAN0(1 A_ ()(1)) A2_ ()(2) 837 | ``` 838 | 839 | You can get around this by using multiple `SCAN` macros and choosing the correct one manually: 840 | 841 | ```c 842 | #define B2_() B2 843 | #define B2(x) SCAN1(A(1)) B2_ EMPTY() ()(x) 844 | // works, but ^--- you need to keep track of depth 845 | SCAN0(B2(2)) // 1 1 1 A_ ()(1) 1 1 A_ ()(1) B2_ ()(2) 846 | ``` 847 | 848 | But there is a potentially better way, by doing a binary search to figure out which `SCAN` macro can be used in the current context: 849 | 850 | ```c 851 | #define IF(x) IF_(x) 852 | #define IF_(x) IF_##x 853 | #define IF_1(a,b) a 854 | #define IF_0(a,b) b 855 | 856 | #define B_TUPLE_AT_1(b,a,...) a 857 | #define IF_C(x) IF(B_TUPLE_AT_1(x,0,)) 858 | 859 | // binary search 860 | #define SCAN IF_C(SCAN1(,1))(IF_C(SCAN0(,1))(SCAN0,SCAN1),IF_C(SCAN2(,1))(SCAN2,SCAN3)) 861 | 862 | 863 | #define C2_() C2 864 | #define C2(x) SCAN(A(1)) C2_ EMPTY() ()(x) 865 | // works, automatically 866 | SCAN(C2(2)) // 1 1 1 A_ ()(1) 1 1 A_ ()(1) C2_ ()(2) 867 | ``` 868 | 869 | 870 | ## O(1) random access memory 871 | 872 | Proof of concept for a constant time random access memory implementation: 873 | 874 | ```c 875 | #define SCAN(...) __VA_ARGS__ 876 | #define FX(f,...) f(__VA_ARGS__) 877 | #define TUPLE_AT_2(a,b,...) b 878 | #define CHECK(...) TUPLE_AT_2(__VA_ARGS__,) 879 | 880 | #define EVAL4(...) EVAL(EVAL(EVAL(__VA_ARGS__))) 881 | #define EVAL(...) __VA_ARGS__ 882 | #define EMPTY() 883 | 884 | 885 | #define MEM_AT_0(a,...) a 886 | #define MEM_AT_1(a,b,...) b 887 | #define MEM_AT_2(a,b,c,...) c 888 | #define MEM_AT_3(a,b,c,d,...) d 889 | #define MEM_AT_4(a,b,c,d,e,...) e 890 | #define MEM_AT_5(a,b,c,d,e,f,...) f 891 | #define MEM_AT_6(a,b,c,d,e,f,g,...) g 892 | #define MEM_AT_7(a,b,c,d,e,f,g,h,...) h 893 | #define MEM_AT_8(a,b,c,d,e,f,g,h,i,...) i 894 | #define MEM_AT_9(a,b,c,d,e,f,g,h,i,j) j 895 | 896 | #define MEM__AT_1_PROBE(...) ,MEM__AT_1 897 | #define MEM__AT_2_PROBE(...) ,MEM__AT_2 898 | #define MEM__AT_3_PROBE(...) ,MEM__AT_3 899 | 900 | #define M16_AT(m,x) FX(M16__AT,m,SCAN x) 901 | #define M16__AT(m,x,...) MEM__AT_4(MEM_AT_##x m,__VA_ARGS__) 902 | #define MEM__AT_4(m,x,...) CHECK(MEM__AT_3_PROBE m,MEM__AT_0)(MEM_AT_##x m,__VA_ARGS__) 903 | #define MEM__AT_3(m,x,...) CHECK(MEM__AT_2_PROBE m,MEM__AT_0)(MEM_AT_##x m,__VA_ARGS__) 904 | #define MEM__AT_2(m,x) CHECK(MEM__AT_1_PROBE m,MEM__AT_0)(MEM_AT_##x m) 905 | #define MEM__AT_1(m) m 906 | #define MEM__AT_0(...) 907 | 908 | 909 | 910 | #define MEM_PUT_0(F,a,b,c,d,e,f,g,h,i,j,...) (F EMPTY()()(a,__VA_ARGS__),b,c,d,e,f,g,h,i,j) 911 | #define MEM_PUT_1(F,a,b,c,d,e,f,g,h,i,j,...) (a,F EMPTY()()(b,__VA_ARGS__),c,d,e,f,g,h,i,j) 912 | #define MEM_PUT_2(F,a,b,c,d,e,f,g,h,i,j,...) (a,b,F EMPTY()()(c,__VA_ARGS__),d,e,f,g,h,i,j) 913 | #define MEM_PUT_3(F,a,b,c,d,e,f,g,h,i,j,...) (a,b,c,F EMPTY()()(d,__VA_ARGS__),e,f,g,h,i,j) 914 | #define MEM_PUT_4(F,a,b,c,d,e,f,g,h,i,j,...) (a,b,c,d,F EMPTY()()(e,__VA_ARGS__),f,g,h,i,j) 915 | #define MEM_PUT_5(F,a,b,c,d,e,f,g,h,i,j,...) (a,b,c,d,e,F EMPTY()()(f,__VA_ARGS__),g,h,i,j) 916 | #define MEM_PUT_6(F,a,b,c,d,e,f,g,h,i,j,...) (a,b,c,d,e,f,F EMPTY()()(g,__VA_ARGS__),h,i,j) 917 | #define MEM_PUT_7(F,a,b,c,d,e,f,g,h,i,j,...) (a,b,c,d,e,f,g,F EMPTY()()(h,__VA_ARGS__),i,j) 918 | #define MEM_PUT_8(F,a,b,c,d,e,f,g,h,i,j,...) (a,b,c,d,e,f,g,h,F EMPTY()()(i,__VA_ARGS__),j) 919 | #define MEM_PUT_9(F,a,b,c,d,e,f,g,h,i,j,...) (a,b,c,d,e,f,g,h,i,F EMPTY()()(j,__VA_ARGS__)) 920 | 921 | #define MEM_PUT_FX(f,...) f(__VA_ARGS__) 922 | #define MEM_PUT_N_PROBE(...) ,MEM_PUT_N_ 923 | #define MEM_PUT_N(F,m,...) CHECK(MEM_PUT_N_PROBE m,MEM_PUT_EMPTY)(F,m,__VA_ARGS__) 924 | #define MEM_PUT_EMPTY(F,m,...) MEM_PUT_N_(F,(,,,,,,,,,),__VA_ARGS__) 925 | #define MEM_PUT_N_(F,m,x,...) MEM_PUT_FX(MEM_PUT_##x,F,SCAN m,__VA_ARGS__) 926 | 927 | #define MEM__PUT_3_ID() MEM__PUT_3 928 | #define MEM__PUT_2_ID() MEM__PUT_2 929 | #define MEM__PUT_1_ID() MEM__PUT_1 930 | #define MEM__PUT_0_ID() MEM__PUT_0 931 | 932 | #define M16_FX(f,...) f(__VA_ARGS__) 933 | #define M16_PUT(m,x,v) EVAL4(FX(M16__PUT,m,SCAN x,v)) 934 | #define M16__PUT(m,x,...) MEM_PUT_N(MEM__PUT_3_ID,m,x,__VA_ARGS__) 935 | #define MEM__PUT_3(m,x,...) MEM_PUT_N(MEM__PUT_2_ID,m,x,__VA_ARGS__) 936 | #define MEM__PUT_2(m,x,...) MEM_PUT_N(MEM__PUT_1_ID,m,x,__VA_ARGS__) 937 | #define MEM__PUT_1(m,x,...) MEM_PUT_N(MEM__PUT_0_ID,m,x,__VA_ARGS__) 938 | #define MEM__PUT_0(m,x) x 939 | 940 | 941 | 942 | M16_PUT(M16_PUT(,(1,2,3,4),X),(1,2,4,0),Y) 943 | // (,(,,(,,,(,,,,X,,,,,),(Y,,,,,,,,,),,,,,),,,,,,,),,,,,,,,) 944 | M16_AT(M16_PUT(,(0,0,0,2),n),(0,0,0,2)) // n 945 | 946 | M16_AT(((,,(,,(0,1,2,3,4,,,,,),,,,,,,),,,,,,,),,,,,,,,,),(0,2,2,2)) // 2 947 | M16_AT(((,,(,,(0,1,2,3,4,,,,,),,,,,,,),,,,,,,),,,,,,,,,),(0,2,2,3)) // 3 948 | ``` 949 | 950 | 951 | ## `__COUNTER__` reset 952 | 953 | 954 | ```c 955 | #define EAT(...) 956 | #define DUMP(...) EAT(__VA_ARGS__) 957 | 958 | #define SEQ_TERMINATE(...) __VA_ARGS__##0 959 | #define INC_A() DUMP(__COUNTER__) INC_B 960 | #define INC_B() DUMP(__COUNTER__) INC_A 961 | #define INC_A0 962 | #define INC_B0 963 | 964 | #define FX(f,x) f x 965 | 966 | #define MEMORISE(n) FX(SEQ_TERMINATE, (INC_A n)) 967 | 968 | #define LIT_0 969 | #define LIT_1 () 970 | #define LIT_2 ()() 971 | #define LIT_3 ()()() 972 | #define LIT_4 ()()()() 973 | #define LIT_5 ()()()()() 974 | #define LIT_6 ()()()()()() 975 | #define LIT_7 ()()()()()()() 976 | #define LIT_8 ()()()()()()()() 977 | #define LIT_9 ()()()()()()()()() 978 | #define LIT_10 ()()()()()()()()()() 979 | 980 | #define MUL_0(x) 981 | #define MUL_1(x) x 982 | #define MUL_2(x) x x 983 | #define MUL_3(x) x x x 984 | #define MUL_4(x) x x x x 985 | #define MUL_5(x) x x x x x 986 | #define MUL_6(x) x x x x x x 987 | #define MUL_7(x) x x x x x x x 988 | #define MUL_8(x) x x x x x x x x 989 | #define MUL_9(x) x x x x x x x x x 990 | #define MUL_10(x) x x x x x x x x x x 991 | 992 | #define MUL(x,y) MUL_##x(LIT_##y) 993 | 994 | int main() { 995 | 996 | MEMORISE(MUL(2,3)) 997 | printf ("%i\n", __COUNTER__); // 6 998 | 999 | // safe line setting 1000 | #0""1 1001 | #line __COUNTER__ 1002 | MEMORISE(MUL(9,5)) 1003 | printf ("%i\n", __COUNTER__ - __LINE__); // 45 1004 | #0""2 1005 | } 1006 | ``` 1007 | 1008 | ## file-function table 1009 | 1010 | `#include` is a file-function table. 1011 | 1012 | Firstly, a file-function depends on a set of Named External Arguments (NEA), which are macros defined prior to the inclusion of the file. A good example of this are Chaos-pp slots, which take `CHAOS_PP_VALUE` as a NEA. The slot assignment file-function then defines 21 macros to produce a memorization of an integer literal. 1013 | 1014 | Secondly, `#include` accepts an MTU. The set of macros considered by this MTU is the domain of the function. The codomain is the set of paths the MTU produces. A good (if extreme) example of this is `#include __DATE__` see [my-ppmptd](https://github.com/JadLevesque/my-ppmptd). 1015 | 1016 | Finally, we can understand a file inclusion as being an MTU indexed function table where the arguments to the function called are all global. 1017 | 1018 | Example with Chaos-pp slots 1019 | ```c 1020 | #define CHAOS_PP_VALUE 5 + 6 // ENA 1021 | #include CHAOS_PP_ASSIGN_SLOT (1) // File-function table indexed at slot number 1 1022 | CHAOS_PP_SLOT (1) // 11 1023 | ``` 1024 | 1025 | 1026 | ## integer arithmetics 1027 | 1028 | * [boline](https://github.com/camel-cdr/boline/blob/main/boline/boline.c) (8/16/32/64 bit integer arithmetics, base 16) 1029 | * [order-pp](https://github.com/rofl0r/chaos-pp/tree/master/chaos/preprocessor/arbitrary) (arbitrary integer arithmetics, base 10) 1030 | 1031 | 1032 | 1033 | 1034 | # The abyss 1035 | 1036 | 1037 | ## Single Instruction/Continuation Multiple Data 1038 | 1039 | Some algorithms can be sped up a lot by processing multiple elements of data in a single continuation, since continuations have a comparatively large overhead. 1040 | 1041 | Reversing tuple for example can be easily done 8 elements at a time: 1042 | 1043 | ```c 1044 | #define E4(...) E3(E3(E3(E3(E3(E3(E3(E3(E3(E3(__VA_ARGS__)))))))))) 1045 | #define E3(...) E2(E2(E2(E2(E2(E2(E2(E2(E2(E2(__VA_ARGS__)))))))))) 1046 | #define E2(...) E1(E1(E1(E1(E1(E1(E1(E1(E1(E1(__VA_ARGS__)))))))))) 1047 | #define E1(...) __VA_ARGS__ 1048 | 1049 | #define EMPTY() 1050 | #define CAT(a,b) CAT_(a,b) 1051 | #define CAT_(a,b) a##b 1052 | #define FX(f,x) f(x) 1053 | #define TUPLE_TAIL(x,...) (__VA_ARGS__) 1054 | #define TUPLE_AT_1(x,y,...) y 1055 | #define CHECK(...) TUPLE_AT_1(__VA_ARGS__,) 1056 | 1057 | // reverse 8 arguments at a time, defer to LOOP is there are less then 8 arguments 1058 | #define SIMD_() SIMD 1059 | #define SIMD_END_END ,SIMD1 1060 | #define SIMD(a,b,c,d,e,f,g,h,...) CHECK(SIMD_END_##h,SIMD0)(a,b,c,d,e,f,g,h,__VA_ARGS__) 1061 | #define SIMD1 LOOP 1062 | #define SIMD0(a,b,c,d,e,f,g,h,...) SIMD_ EMPTY() ()(__VA_ARGS__),h,g,f,e,d,c,b,a 1063 | 1064 | // reverse 1 argument at a time 1065 | #define LOOP_() LOOP 1066 | #define LOOP_END_END ,LOOP1 1067 | #define LOOP(x,...) CHECK(LOOP_END_##x,LOOP0)(x,__VA_ARGS__) 1068 | #define LOOP1(x,...) 1069 | #define LOOP0(x,...) LOOP_ EMPTY() ()(__VA_ARGS__),x 1070 | 1071 | #define REVERSE(...) FX(TUPLE_TAIL,E4(SIMD(__VA_ARGS__,END,END,END,END,END,END,END,END))) 1072 | 1073 | REVERSE(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15) 1074 | // (15,14,13,12,11,10,9,8,7,6,5,4,3,2,1) 1075 | ``` 1076 | 1077 | ## `ICE_P()` 1078 | 1079 | You can overload a function depending on one of the argument being a constant expression. 1080 | 1081 | It works by abusing the fact that the return type of the ternary expression `1 ? (void*)1 : (int*)1` has the type `void*`, but `1 ? (void*)0 : (int*)1` has the type `int*`: 1082 | 1083 | > [...] if one operand is a null pointer constant, the result has the type of the other operand; otherwise, one operand is a pointer to void or a qualified version of void, in which case the result type is a pointer to an appropriately qualified version of void. 1084 | 1085 | (https://port70.net/~nsz/c/c11/n1570.html#6.5.15p6) 1086 | 1087 | Since any constant expression that evaluates to zero is a null pointer constant, you can e.g. do the following: 1088 | 1089 | ```c 1090 | #define ICE_P(x) _Generic((1 ? ((void*)!(x)) : &(int){1}), int*: 1, void*: 0) 1091 | #define pow(x,p) (ICE_P(p==1 || p==2) ? (p==1 ? x : (p==2 ? x*x : 0)) : pow(x, p)) 1092 | 1093 | pow(x,1); // (x) 1094 | pow(x,2); // (x*x) 1095 | pow(x,y); // pow(x,y) 1096 | ``` 1097 | 1098 | (https://godbolt.org/z/bjo5TcnbT) 1099 | 1100 | 1101 | ## macro expansion can be recursive (extension) 1102 | GCC exploit up to version 11.3 1103 | 1104 | Macros can be recursive through the macro revival mechanism (term coined by Foundry). This mechanism clears the recursion inhibiting paint. This can be pushed much further like demonstrated in the now deprecated [Grail-pp](https://github.com/JadLevesque/Grail-pp) 1105 | 1106 | ```c 1107 | #2""3 1108 | 1109 | #define PRAGMA(...) _Pragma(#__VA_ARGS__) 1110 | #define REVIVE(m) PRAGMA(push_macro(#m))PRAGMA(pop_macro(#m)) 1111 | #define DEC(n,...) (__VA_ARGS__) 1112 | #define FX(f,x) REVIVE(FX) f x 1113 | #define HOW_MANY_ARGS(...) REVIVE(HOW_MANY_ARGS) \ 1114 | __VA_OPT__(+1 FX(HOW_MANY_ARGS, DEC(__VA_ARGS__))) 1115 | 1116 | int main () { 1117 | printf("%i", HOW_MANY_ARGS(1,2,3,4,5)); // 5 1118 | } 1119 | ``` 1120 | 1121 | ## [`#include` custom filesystem](https://github.com/camel-cdr/execfs) 1122 | 1123 | ## `F(x,y)y)y)y)y)` 1124 | 1125 | A guide is a group of adjacent elements separated by closing parentheses, e.g. `1)2)3))(),w,())awoo()),())`. 1126 | 1127 | Guides are often constructed in place or from a [preprocessor sequence](#sequence-datastructure): 1128 | 1129 | ```c 1130 | #define SEQ_TERM(...) SEQ_TERM_(__VA_ARGS__) 1131 | #define SEQ_TERM_(...) __VA_ARGS__##_RM 1132 | 1133 | #define EMPTY() 1134 | #define RPAREN() ) 1135 | 1136 | #define TO_GUIDE_A(...) __VA_ARGS__ RPAREN EMPTY()()TO_GUIDE_B 1137 | #define TO_GUIDE_B(...) __VA_ARGS__ RPAREN EMPTY()()TO_GUIDE_A 1138 | #define TO_GUIDE_A_RM 1139 | #define TO_GUIDE_B_RM 1140 | #define TO_GUIDE(seq) SEQ_TERM(TO_GUIDE_A seq) 1141 | 1142 | TO_GUIDE((1)(2)(3)(4)(5)) // 1 )2 )3 )4 )5 ) 1143 | ``` 1144 | 1145 | The main advantage of the using guides is that they allow for fast iteration, very similar to the sequence iteration, but they also support passing a context between iterations, e.g.: 1146 | 1147 | ```c 1148 | #define TUPLE_AT_1(x,y,...) y 1149 | #define CHECK(...) TUPLE_AT_1(__VA_ARGS__,) 1150 | 1151 | #define CAT_GUIDE_END_END ,CAT_GUIDE_END 1152 | #define CAT_GUIDE_A(ctx,x) CHECK(CAT_GUIDE_END_##x,CAT_GUIDE_NEXT)(ctx,x,B) 1153 | #define CAT_GUIDE_B(ctx,x) CHECK(CAT_GUIDE_END_##x,CAT_GUIDE_NEXT)(ctx,x,A) 1154 | #define CAT_GUIDE_NEXT(ctx,x,next) CAT_GUIDE_##next(ctx##x, 1155 | #define CAT_GUIDE_END(ctx,x,next) ctx 1156 | 1157 | #define CAT_GUIDE(guide) CAT_GUIDE_A(,guide 1158 | #define CAT_SEQ(seq) CAT_GUIDE(TO_GUIDE(seq(END))) 1159 | 1160 | CAT_SEQ((1)(2)(3)(4)(5)) // 12345 1161 | ``` 1162 | 1163 | The above has the same portability problems as [preprocessor sequence](#sequence-datastructure), as in it may be interpreted as a nested expansion and hence may not work on all preprocessors (although all major ones support it). 1164 | 1165 | But guides can also be used, probably, by using a fixed length chain of iteration macros. The following code showcases this by implementing 8-bit integer addition: 1166 | 1167 | ```c 1168 | #define ADD_000(f) 0 f(0, 1169 | #define ADD_001(f) 1 f(0, 1170 | #define ADD_010(f) 1 f(0, 1171 | #define ADD_011(f) 0 f(1, 1172 | #define ADD_100(f) 1 f(0, 1173 | #define ADD_101(f) 0 f(1, 1174 | #define ADD_110(f) 0 f(1, 1175 | #define ADD_111(f) 1 f(1, 1176 | #define ADD_(c,x,y,n) ADD_##c##x##y(ADD_##n) 1177 | 1178 | #define ADD_8(c,x,y) ADD_(c,x,y,7) 1179 | #define ADD_7(c,x,y) ,ADD_(c,x,y,6) 1180 | #define ADD_6(c,x,y) ,ADD_(c,x,y,5) 1181 | #define ADD_5(c,x,y) ,ADD_(c,x,y,4) 1182 | #define ADD_4(c,x,y) ,ADD_(c,x,y,3) 1183 | #define ADD_3(c,x,y) ,ADD_(c,x,y,2) 1184 | #define ADD_2(c,x,y) ,ADD_(c,x,y,1) 1185 | #define ADD_1(c,x,y) ,ADD_(c,x,y,0) 1186 | #define ADD_0(c,x,y) 1187 | 1188 | #define FX(f,...) f(__VA_ARGS__) 1189 | #define SCAN(...) __VA_ARGS__ 1190 | #define ADD_8BIT(x,y) (FX(ADD_8BIT_,SCAN x, SCAN y)) 1191 | #define ADD_8BIT_(x0,x1,x2,x3,x4,x5,x6,x7,y0,y1,y2,y3,y4,y5,y6,y7) \ 1192 | ADD_8(0,x0,y0)x1,y1)x2,y2)x3,y3)x4,y4)x5,y5)x6,y6)x7,y7),) 1193 | 1194 | // bits are reversed (LSB,...,MSB): 1195 | ADD_8BIT((0,1,0,1,0,1,0,0),(0,0,1,0,0,1,1,0)) // (0,1,1,1,0,0,0,1) 1196 | // 0 0 1 0 1 0 1 0 + 0 1 1 0 0 1 0 0 = 1 0 0 0 1 1 1 0 1197 | // 42 + 100 = 142 1198 | ``` 1199 | 1200 | 1201 | ## [`#for #endfor`](http://www2.open-std.org/JTC1/SC22/WG14/www/docs/n1410.pdf) rejected C proposal 1202 | 1203 | 1204 | 1205 | ## operator overloading 1206 | 1207 | 1208 | 1209 | 1244 | 1245 | 1268 |
"main.c""calc.c"
1210 | 1211 | ```c 1212 | #define SLOT (2,==,2) 1213 | #include "calc.c" 1214 | VAL // 1 1215 | 1216 | #define SLOT (1,<=>,12) 1217 | #include "calc.c" 1218 | VAL // -1 1219 | #define SLOT (12,<=>,12) 1220 | #include "calc.c" 1221 | VAL // 0 1222 | #define SLOT (12,<=>,1) 1223 | #include "calc.c" 1224 | VAL // 1 1225 | ``` 1226 | 1227 | 1228 | 1229 | ```c 1230 | #undef VAL 1231 | #define SLOT_1(a,b,c) a 1232 | #define SLOT_2(a,b,c) #b 1233 | #define SLOT_3(a,b,c) c 1234 | 1235 | #define SCAN(...) __VA_ARGS__ 1236 | #define LHS SCAN(SLOT_1 SLOT) 1237 | #define RHS SCAN(SLOT_3 SLOT) 1238 | #include SCAN(SLOT_2 SLOT) 1239 | 1240 | #undef SLOT 1241 | ``` 1242 | 1243 |
"<=>""=="
1246 | 1247 | ```c 1248 | #if LHS < RHS 1249 | #define VAL -1 1250 | #elif LHS == RHS 1251 | #define VAL 0 1252 | #else 1253 | #define VAL 1 1254 | #endif 1255 | ``` 1256 | 1257 | 1258 | 1259 | ```c 1260 | #if LHS == RHS 1261 | #define VAL 1 1262 | #else 1263 | #define VAL 0 1264 | #endif 1265 | ``` 1266 | 1267 |
1269 | 1270 | 1271 | ## tcc's non-recursive expansion is recursive 1272 | 1273 | ```c 1274 | #define A(x) x B 1275 | #define B(x) x A 1276 | A(1)(1)(1)(1) 1277 | ``` 1278 | 1279 | The standard says that it's implementation defined if in the above code the macro expansions are nested or not. (see https://port70.net/~nsz/c/c11/n1570.html#6.10.3.4p4 and "When a fully expanded..." in Annex J) 1280 | So an implementation could expand the above either to "1 1 A(1)(1)..." or "1 1 1 1 A". 1281 | 1282 | gcc, clang, tcc and all otherwise valid preprocessor implementation I know of expand it to "1 1 1 1 A", which is great for preprocessor meta programming. 1283 | 1284 | But the problem is, whiles tcc expands the macros as though the expansion isn't nested, this isn't reflected in the tcc code. 1285 | Meaning, if instead of 4 iterations you have e.g. 20000 of them tcc segfaults, and the backtrace indicates that it's a stack overflow because of too many recursive calls. 1286 | 1287 | So tcc implements non-recursive expansion recursively. 1288 | 1289 | 1290 | ## slots 1291 | 1292 | Slots are a technique to evaluate a constant expression and turn it into an independent macro definition. It effectively allows for cross-MTU memory. 1293 | 1294 | 1295 | 1296 | 1344 |
example.cslot.c
1297 | 1298 | ```c 1299 | #define SLOT (2*3+1) 1300 | #include "slot.c" 1301 | VAL // 7 1302 | 1303 | // slot.c needs A and B parts to 1304 | // allow for self-referential SLOTs: 1305 | #undef SLOT 1306 | #define SLOT (VAL - 1) 1307 | #include "slot.c" 1308 | VAL // 6 1309 | #include "slot.c" 1310 | VAL // 5 1311 | #include "slot.c" 1312 | VAL // 4 1313 | 1314 | #undef SLOT 1315 | #define SLOT ((99|87) > (99^87)) 1316 | #include "slot.c" 1317 | VAL // 1 1318 | ``` 1319 | 1320 | 1321 | 1322 | ```c 1323 | #ifndef VAL_000 1324 | # define VAL_000 0 1325 | # define VAL_001 1 1326 | # define VAL_010 2 1327 | # define VAL_011 3 1328 | # define VAL_100 4 1329 | # define VAL_101 5 1330 | # define VAL_110 6 1331 | # define VAL_111 7 1332 | # define VAL_C_(a,b,c) VAL_##a##b##c 1333 | # define VAL_C(a,b,c) VAL_C_(a,b,c) 1334 | #endif 1335 | 1336 | #ifndef A_0 1337 | # include "a.c" 1338 | #else 1339 | # include "b.c" 1340 | #endif 1341 | ``` 1342 | 1343 |
a.cb.c
1345 | 1346 | ```c 1347 | #if (SLOT) & 1 1348 | # define A_0 1 1349 | #else 1350 | # define A_0 0 1351 | #endif 1352 | #if (SLOT) & 2 1353 | # define A_1 1 1354 | #else 1355 | # define A_1 0 1356 | #endif 1357 | #if (SLOT) & 4 1358 | # define A_2 1 1359 | #else 1360 | # define A_2 0 1361 | #endif 1362 | 1363 | #undef VAL 1364 | #define VAL VAL_C(A_2,A_1,A_0) 1365 | 1366 | #undef B_0 1367 | #undef B_1 1368 | #undef B_2 1369 | ``` 1370 | 1371 | 1372 | 1373 | ```c 1374 | #if (SLOT) & 1 1375 | # define B_0 1 1376 | #else 1377 | # define B_0 0 1378 | #endif 1379 | #if (SLOT) & 2 1380 | # define B_1 1 1381 | #else 1382 | # define B_1 0 1383 | #endif 1384 | #if (SLOT) & 4 1385 | # define B_2 1 1386 | #else 1387 | # define B_2 0 1388 | #endif 1389 | 1390 | #undef VAL 1391 | #define VAL VAL_C(B_2,B_1,B_0) 1392 | 1393 | #undef A_0 1394 | #undef A_1 1395 | #undef A_2 1396 | ``` 1397 | 1398 |
1399 | 1400 | 1401 | ## file iteration 1402 | 1403 | File iteration can be done using methods of cross-MTU memory, like [slots](#slots): 1404 | 1405 | 1406 | 1407 |
fileoutput
1408 | 1409 | ```c 1410 | #ifndef VAL 1411 | #define VAL 7 1412 | #endif 1413 | 1414 | VAL 1415 | 1416 | #define SLOT (VAL-1) 1417 | #include "slot.c" 1418 | #if VAL != 0 1419 | #include __FILE__ 1420 | #endif 1421 | ``` 1422 | 1423 | 1424 | 1425 | ```c 1426 | 7 1427 | 6 1428 | 5 1429 | 4 1430 | 3 1431 | 2 1432 | 1 1433 | ``` 1434 | 1435 |
1436 | 1437 | 1438 | The above example uses nested/self inclusion, but the standard only mandates "15 nesting levels for #included files". ([C11](https://port70.net/~nsz/c/c11/n1570.html#5.2.4.1p1)) 1439 | 1440 | By default, gcc only allows for up to` 200` nested inclusions, but this limit can be extended arbitrarily using the `-fmax-include-depth=N` command line argument. 1441 | 1442 | A more portable, but also more verbose approach is to create a flat inclusion machine that supports a finite number of iterations, but doesn't nest more than 15 times. [chaos-pp](#chaos-pp) has [such a machine](https://github.com/rofl0r/chaos-pp/blob/master/chaos/preprocessor/iteration/detail/i1.h): 1443 | 1444 | 1445 | ```c 1446 | // file.h 1447 | #if !CHAOS_PP_IS_ITERATING 1448 | 1449 | #ifndef FILE_H 1450 | #define FILE_H 1451 | 1452 | #include 1453 | #include 1454 | 1455 | 1456 | #define ITERATION_FILE "file.h" 1457 | 1458 | #define RADIUS 10 1459 | 1460 | 1461 | #define ORDER_PP_DEF_8radius ORDER_PP_CONST (RADIUS) 1462 | #define ORDER_PP_DEF_8height ORDER_PP_CONST (CHAOS_PP_ITERATION()) 1463 | 1464 | 1465 | #define ORDER_PP_DEF_8output ORDER_PP_FN \ 1466 | (8fn (8R, 8Y, 8X \ 1467 | ,8print (8if (8equation, 8quote (%), 8quote (~)) \ 1468 | 8space \ 1469 | ) \ 1470 | ) \ 1471 | ) 1472 | 1473 | /** 1474 | circle 1475 | x*x + y*y <= r*r 1476 | */ 1477 | #define ORDER_PP_DEF_8equation ORDER_PP_MACRO \ 1478 | (8less_eq (8add (8pow (8X, 2) \ 1479 | ,8pow (8Y, 2) \ 1480 | ) \ 1481 | ,8pow (8R, 2) \ 1482 | ) \ 1483 | ) 1484 | 1485 | 1486 | #define CHAOS_PP_ITERATION_PARAMS (RADIUS)(0)(ITERATION_FILE) 1487 | %:include CHAOS_PP_ITERATE() 1488 | 1489 | #define CHAOS_PP_ITERATION_PARAMS (0)(RADIUS)(ITERATION_FILE) 1490 | %:include CHAOS_PP_ITERATE() 1491 | 1492 | 1493 | #endif // FILE_H 1494 | 1495 | #else 1496 | 1497 | ORDER_PP 1498 | (8do (8seq_for_each (8output (8radius, 8height) 1499 | ,8seq_iota(8inc (8radius), 0) 1500 | ) 1501 | ,8seq_for_each (8output (8radius, 8height) 1502 | ,8seq_iota(0, 8inc (8radius)) 1503 | ) 1504 | ) 1505 | ) 1506 | 1507 | #endif 1508 | ``` 1509 | Output: 1510 | ```c 1511 | ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ % % ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ 1512 | ~ ~ ~ ~ ~ ~ % % % % % % % % % % ~ ~ ~ ~ ~ ~ 1513 | ~ ~ ~ ~ % % % % % % % % % % % % % % ~ ~ ~ ~ 1514 | ~ ~ ~ % % % % % % % % % % % % % % % % ~ ~ ~ 1515 | ~ ~ % % % % % % % % % % % % % % % % % % ~ ~ 1516 | ~ ~ % % % % % % % % % % % % % % % % % % ~ ~ 1517 | ~ % % % % % % % % % % % % % % % % % % % % ~ 1518 | ~ % % % % % % % % % % % % % % % % % % % % ~ 1519 | ~ % % % % % % % % % % % % % % % % % % % % ~ 1520 | ~ % % % % % % % % % % % % % % % % % % % % ~ 1521 | % % % % % % % % % % % % % % % % % % % % % % 1522 | % % % % % % % % % % % % % % % % % % % % % % 1523 | ~ % % % % % % % % % % % % % % % % % % % % ~ 1524 | ~ % % % % % % % % % % % % % % % % % % % % ~ 1525 | ~ % % % % % % % % % % % % % % % % % % % % ~ 1526 | ~ % % % % % % % % % % % % % % % % % % % % ~ 1527 | ~ ~ % % % % % % % % % % % % % % % % % % ~ ~ 1528 | ~ ~ % % % % % % % % % % % % % % % % % % ~ ~ 1529 | ~ ~ ~ % % % % % % % % % % % % % % % % ~ ~ ~ 1530 | ~ ~ ~ ~ % % % % % % % % % % % % % % ~ ~ ~ ~ 1531 | ~ ~ ~ ~ ~ ~ % % % % % % % % % % ~ ~ ~ ~ ~ ~ 1532 | ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ % % ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ 1533 | ``` 1534 | 1535 | 1536 | ## file stack 1537 | 1538 | Note: GCC only. 1539 | 1540 | GCC stores file names in a 200 high stack. Information stored on this stack includes file name and line number. Using linemarkers, it is possible to push, pop and modify this stack. 1541 | 1542 | There are a few subtilities to take into consideration (which happen to vary with GCC version), but the essential is: 1543 | - Push: `#` <*line number*> <*file name*> `1` 1544 | - Pop: `#` <*line number*> <*file name*> `2` 1545 | 1546 | 1547 | There are small but important details to keep into consideration for the proper manipulation of the file stack. Henceforth, variables will be used to succinctly refer to file names. 1548 | 1549 | Note: the line number must be a literal. The linemarker does not accept an MTU 1550 | 1551 | ### Push 1552 | 4.1.2 - 12.1 1553 | `# any-file-name 1` 1554 | 1555 | Example 1556 | ```c 1557 | #define INFO [__INCLUDE_LEVEL__, __LINE__, __FILE__] 1558 | #line 1 "foo.c" 1559 | INFO 1560 | #42 "bar.h" 1 1561 | INFO 1562 | #123 "baz.h" 1 1563 | INFO 1564 | ``` 1565 | Output: 1566 | ``` 1567 | [0, 1, "foo.c"] 1568 | [1, 42, "bar.h"] 1569 | [2, 123, "baz.h"] 1570 | ``` 1571 | 1572 | Shape of the stack: 1573 | ```c 1574 | +-------+ 1575 | 2 | baz.h | 1576 | +-------+ 1577 | 1 | bar.h | 1578 | +-------+ 1579 | 0 | foo.c | 1580 | +-------+ 1581 | ``` 1582 | 1583 | 1584 | ### Pop 1585 | Behaviour in function of version: 1586 | 1587 | | Version range | With correct file name | With wrong file name | With empty file name | 1588 | | :--- | :--- | :--- | :--- | 1589 | | 4.1.2 - 5.4 | Without carry | With carry | With carry | 1590 | | 6.1 - 9.5 | Without carry | Ignored | Ignored | 1591 | | 10.1 - 12.1 | Without carry | Ignored | Without carry | 1592 | 1593 | 1594 | ### Pop with carry 1595 | 4.1.2 - 5.4 1596 | Example: 1597 | ```c 1598 | // in "/app/example.c" 1599 | #define INFO [__INCLUDE_LEVEL__, __LINE__, __FILE__] 1600 | #line 1 1601 | #1 "bar.h" 1 // push "bar.h" 1602 | INFO 1603 | #42 "123" 2 // pop with a file name different than "/app/example.c" 1604 | INFO 1605 | ``` 1606 | Output: 1607 | ```c 1608 | [1, 1, "bar.h"] 1609 | [0, 2, "/app/example.c"] 1610 | ``` 1611 | 1612 | ### Pop with without carry 1613 | 4.1.2 - 12.1 1614 | ```c 1615 | #define INFO [__INCLUDE_LEVEL__, __LINE__, __FILE__] 1616 | // in "/app/example.c" 1617 | INFO 1618 | #2"foo.h"1 1619 | INFO 1620 | #42"/app/example.c"2 // correct file name 1621 | INFO 1622 | ``` 1623 | Output: 1624 | ```c 1625 | [0, 3, "/app/example.c"] 1626 | [1, 2, "foo.h"] 1627 | [0, 42, "/app/example.c"] 1628 | ``` 1629 | 1630 | 10.1 - 12.1 1631 | ```c 1632 | #define INFO [__INCLUDE_LEVEL__, __LINE__, __FILE__] 1633 | // in "/app/example.c" 1634 | INFO 1635 | #2"foo.h"1 1636 | INFO 1637 | #42""2 // empty file name 1638 | INFO 1639 | ``` 1640 | Output: 1641 | ```c 1642 | [0, 3, "/app/example.c"] 1643 | [1, 2, "foo.h"] 1644 | [0, 42, "/app/example.c"] 1645 | ``` 1646 | 1647 | ### Pop ignored 1648 | 6.1 - 12.1 1649 | ```c 1650 | #define INFO [__INCLUDE_LEVEL__, __LINE__, __FILE__] 1651 | // in "/app/example.c" 1652 | INFO 1653 | #2"foo.h"1 1654 | INFO 1655 | #42"bar.h"2 // wrong file name 1656 | INFO 1657 | ``` 1658 | Output: 1659 | ```c 1660 | [0, 3, "/app/example.c"] 1661 | [1, 2, "foo.h"] 1662 | [1, 4, "foo.h"] 1663 | ``` 1664 | 1665 | 6.1 - 9.5 1666 | ```c 1667 | #define INFO [__INCLUDE_LEVEL__, __LINE__, __FILE__] 1668 | // in "/app/example.c" 1669 | INFO 1670 | #2"foo.h"1 1671 | INFO 1672 | #42""2 // empty file name 1673 | INFO 1674 | ``` 1675 | Output: 1676 | ```c 1677 | [0, 3, "/app/example.c"] 1678 | [1, 2, "foo.h"] 1679 | [1, 4, "foo.h"] 1680 | ``` 1681 | 1682 | 1683 | 1684 | ## file stack line accumulator 1685 | 1686 | Available for GCC up to version 5.4. 1687 | 1688 | The file stack line accumulator exploits a behaviour of the pop linemarker to continue the line count of the rentered file (see [pop linemarker](#pop)). The exploitation of this behaviour combined with careful file nesting and file recursion means the line counter can be used as an accumulator. Since the line count increases with each new line, line arithmetic becomes an art of position-oriented programming where each line is important. This makes for rather convoluted and seldom readable programs, as every line is important and insertion of comments would break the careful arrangment of directives and empty space. 1689 | 1690 | Possible operations include reseting the accumulator (`#line somevalue`), incrementation (newline), multiplication (multiple nested addition, line slot, reseting accum with current line and concatenated zeros for pow 10). Substraction is possible through overflowing addition. 1691 | 1692 | ```c 1693 | #0"stdio.h"3 1694 | /*************** 1695 | * They say comments don't affect the result of compilation... 1696 | * This comment disagrees! 1697 | * For every new line, this comment adds results to the output. 1698 | * Try it... 1699 | * Seriously, try it! 1700 | * (only works with GCC of versions 5.4 and earlier) 1701 | */ 1702 | #ifdef stdin 1703 | #if __INCLUDE_LEVEL__ != __LINE__ 1704 | #0""2 1705 | ... 1706 | ``` 1707 | 1708 | 1709 | 1845 |
1710 | 1711 | ```c 1712 | #if TEST(0,0) 1713 | #define DIGIT_0 0 1714 | #elif TEST(0,1) 1715 | #define DIGIT_0 1 1716 | #elif TEST(0,2) 1717 | #define DIGIT_0 2 1718 | #elif TEST(0,3) 1719 | #define DIGIT_0 3 1720 | #elif TEST(0,4) 1721 | #define DIGIT_0 4 1722 | #elif TEST(0,5) 1723 | #define DIGIT_0 5 1724 | #elif TEST(0,6) 1725 | #define DIGIT_0 6 1726 | #elif TEST(0,7) 1727 | #define DIGIT_0 7 1728 | #elif TEST(0,8) 1729 | #define DIGIT_0 8 1730 | #else 1731 | #define DIGIT_0 9 1732 | #endif 1733 | // important 1734 | ``` 1735 | 1736 | 1737 | 1738 | ```c 1739 | #if TEST(1,0) 1740 | #define DIGIT_1 0 1741 | #elif TEST(1,1) 1742 | #define DIGIT_1 1 1743 | #elif TEST(1,2) 1744 | #define DIGIT_1 2 1745 | #elif TEST(1,3) 1746 | #define DIGIT_1 3 1747 | #elif TEST(1,4) 1748 | #define DIGIT_1 4 1749 | #elif TEST(1,5) 1750 | #define DIGIT_1 5 1751 | #elif TEST(1,6) 1752 | #define DIGIT_1 6 1753 | #elif TEST(1,7) 1754 | #define DIGIT_1 7 1755 | #elif TEST(1,8) 1756 | #define DIGIT_1 8 1757 | #else 1758 | #define DIGIT_1 9 1759 | #endif 1760 | // important 1761 | ``` 1762 | 1763 | 1764 | 1765 | ```c 1766 | #if TEST(2,0) 1767 | #define DIGIT_2 0 1768 | #elif TEST(2,1) 1769 | #define DIGIT_2 1 1770 | #elif TEST(2,2) 1771 | #define DIGIT_2 2 1772 | #elif TEST(2,3) 1773 | #define DIGIT_2 3 1774 | #elif TEST(2,4) 1775 | #define DIGIT_2 4 1776 | #elif TEST(2,5) 1777 | #define DIGIT_2 5 1778 | #elif TEST(2,6) 1779 | #define DIGIT_2 6 1780 | #elif TEST(2,7) 1781 | #define DIGIT_2 7 1782 | #elif TEST(2,8) 1783 | #define DIGIT_2 8 1784 | #else 1785 | #define DIGIT_2 9 1786 | #endif 1787 | // important 1788 | ``` 1789 | 1790 | 1791 | 1792 | ```c 1793 | #if TEST(3,0) 1794 | #define DIGIT_3 0 1795 | #elif TEST(3,1) 1796 | #define DIGIT_3 1 1797 | #elif TEST(3,2) 1798 | #define DIGIT_3 2 1799 | #elif TEST(3,3) 1800 | #define DIGIT_3 3 1801 | #elif TEST(3,4) 1802 | #define DIGIT_3 4 1803 | #elif TEST(3,5) 1804 | #define DIGIT_3 5 1805 | #elif TEST(3,6) 1806 | #define DIGIT_3 6 1807 | #elif TEST(3,7) 1808 | #define DIGIT_3 7 1809 | #elif TEST(3,8) 1810 | #define DIGIT_3 8 1811 | #else 1812 | #define DIGIT_3 9 1813 | #endif 1814 | // important 1815 | ``` 1816 | 1817 | 1818 | 1819 | ```c 1820 | #if TEST(4,0) 1821 | #define DIGIT_4 0 1822 | #elif TEST(4,1) 1823 | #define DIGIT_4 1 1824 | #elif TEST(4,2) 1825 | #define DIGIT_4 2 1826 | #elif TEST(4,3) 1827 | #define DIGIT_4 3 1828 | #elif TEST(4,4) 1829 | #define DIGIT_4 4 1830 | #elif TEST(4,5) 1831 | #define DIGIT_4 5 1832 | #elif TEST(4,6) 1833 | #define DIGIT_4 6 1834 | #elif TEST(4,7) 1835 | #define DIGIT_4 7 1836 | #elif TEST(4,8) 1837 | #define DIGIT_4 8 1838 | #else 1839 | #define DIGIT_4 9 1840 | #endif 1841 | // important 1842 | ``` 1843 | 1844 |
1846 | 1847 | ```c 1848 | ... 1849 | #0""1 1850 | #0""3 1851 | #line RAW_SLOT() // recording the accumulator for later 1852 | #line __LINE__ STR(__LINE__) // storing the value of the accumulator as a string. no effect on the accumulator 1853 | __FILE__", " 1854 | %:include __BASE_FILE__ //"" 1855 | #endif // why ^^^^^^^^^^^^^ here but 1856 | #else // not vvvvvvvv here? because magic 1857 | #include __FILE__ //"" // <- the comment to the left is a sacrifice to appease the godbolt syntax highlighter 1858 | #define CAT_5_PR(a,b,c,d,e) a##b##c##d##e 1859 | #define STR(v...) STR_PR(v) 1860 | #define STR_PR(v...) #v 1861 | #define FX(f,x) f x 1862 | 1863 | #define EXP_0 1UL 1864 | #define EXP_1 10UL 1865 | #define EXP_2 100UL 1866 | #define EXP_3 1000UL 1867 | #define EXP_4 10000UL 1868 | 1869 | #define TEST(d,v) ((VALUE(BUFFER(d,v))) / EXP_##d) % 10 == v 1870 | // Raw slots only have their place in line slot. 1871 | // This is because line temporary memorisation removes leading zeros. 1872 | #define RAW_SLOT() FX(CAT_5_PR, (DIGIT_4, DIGIT_3, DIGIT_2, DIGIT_1, DIGIT_0)) 1873 | #define BUFFER(d,v) (__LINE__ - (2 * v + 22 * d + 2)) 1874 | #define VALUE(buffer) buffer * 2 1875 | int main(){printf("powers of 2: " 1876 | #1""3 // initialization of the line accumulator to 1 1877 | // important 1878 | %:include __BASE_FILE__ 1879 | "...");} 1880 | #endif 1881 | ``` 1882 | 1883 | (https://godbolt.org/z/Kszj7K9h4) 1884 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The Preprocessor Iceberg Meme 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 107 | 108 | 109 |

The Preprocessor Iceberg Meme

110 | 111 |

Preprocessor metaprogramming knowledge obscurity chart

112 |

contributors:
JadLévesque
notfoundry
camel-cdr

113 |

Based on Blackle Mori's (@suricrasia) The Cursed Computer Iceberg Meme

114 | 115 |
116 | 117 |
118 |

Above the iceberg

119 | 129 |
130 | 131 |
132 |

On the iceberg

133 | 144 |
145 | 146 |
147 |

Below the water

148 | 162 |
163 | 164 |
165 |

Middle of the iceberg

166 | 180 |
181 | 182 | 195 | 196 |
197 |

Below the iceberg

198 | 212 |
213 | 214 | 232 | 233 |
234 |

The abyss

235 | 249 |
250 | 251 | 252 | 253 | 254 | --------------------------------------------------------------------------------