“the () in macros is like a condom basically”
— Unknown
Cool tricks in the C programming language.
The only reasons these tricks exist is because C is shitty and old. The tricks are still cool, though.
1. Sized enum macro
#include <stdio.h>
#include <stdint.h>
#define ENUM(type, name) type name; enum
typedef ENUM(uint8_t, Thing) {
THING_FOO,
THING_BAR,
THING_BAZ,
};
struct State {
Thing thing;
/* ... */
};
int main() {
printf("%zu\n", sizeof(Thing)); // 1
return 0;
}
2. Inline shader code
#include <stdio.h>
#define SHADER(...) #__VA_ARGS__
const char *shader = SHADER(
\x23version 330 core\n
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
);
int main() {
printf("%s\n", shader); // #version 330 core
// void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }
return 0;
}
3. Optional function parameters
C macro approach to using optional / default / named function params, using struct designated initializers.
Mandatory values are passed first as standard function params (rect in the example).
Especially useful when a zero value can be made a valid value.
typedef struct {
Point pivot;
float scale;
float angle;
bool clip;
} Transform_Opt;
void transform_opt(Rect rect, Transform_Opt opt) {
/* ... */
}
#define transform(rect, ...) transform_opt((rect), (Transform_Opt){ .scale = 1.0, __VA_ARGS__ })
int main() {
/* ... */
transform(rect);
transform(rect, .scale = 0.5);
transform(rect, .angle = 90.0, .clip = true);
transform_opt(rect, opt);
return 0;
}
4. Type only passable by a pointer
#include <assert.h>
typedef char foo[1]; // Defines a type foo that is impossible to pass by value...
void bar(foo x) // ...because an array decays to a pointer here.
{
assert(sizeof(x) == 8);
}
int main()
{
foo x;
assert(sizeof(x) == 1);
bar(x);
return 0;
}