cum.h
cum.h is a set of Cursed yet Useful Macros for C and C++: recursive variadic macros, a type-agnostic dynamic array, a command runner, a stack, and small helpers. It is a single header, public domain.
-std=gnu99 or -std=gnu++11:
it relies on GNU extensions such as statement expressions,
__auto_type, __typeof__ and
__VA_OPT__.Recursive macros
The C preprocessor does not allow recursion: while a macro is being expanded, its own name is "painted blue" and any appearance of it is left alone. cum.h works around this with the classic deferred-expansion trick:
#define _EMPTY() #define _DEFER(_id_) _id_ _EMPTY() #define OBSTRUCT(_id_) _id_ _DEFER(_EMPTY)() #define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) #define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) ... #define EVAL5(...) __VA_ARGS__
OBSTRUCT(M)()hides the call toMso it is not expanded during the current scan, only in a later one. At that point the macro is no longer "in use", so it can expand again.EVALforces the preprocessor to rescan its argument many times: every level triples the previous one, for 3⁵ = 243 scans. Each scan unrolls one step of the recursion.
A recursive macro is then written as an INNER macro that
handles the first argument and calls itself indirectly through an
INDIRECT macro, while __VA_OPT__ stops the
recursion when there are no arguments left:
#define Join(_op_, ...) __VA_OPT__(EVAL(JOIN_INNER(_op_, __VA_ARGS__))) #define JOIN_INNER(_op_, a, ...) a __VA_OPT__(_op_ OBSTRUCT(JOIN_INDIRECT)()(_op_, __VA_ARGS__)) #define JOIN_INDIRECT() JOIN_INNER
Join
Join(op, a, b, c) expands to
a op b op c:
int sum = Join(+, x, y, z); // x + y + z if (Join(&&, ok1, ok2, ok3)) ... // ok1 && ok2 && ok3
If the operator is a comma, use __VA_ARGS__
directly.
Unused
Unused(a, b, c) expands to
(void) a; (void) b; (void) c;, silencing unused-variable
warnings for any number of names.
Command_add
Appends any number of arguments to a Command, one
Da_append each:
Command cmd = { 0 };
Command_add(&cmd, "cc", "-o", "main", "main.c");
Dynamic array
Da(type) declares an anonymous struct with
capacity, count and items:
Da(int) nums = { 0 };
Da_append(&nums, 42);
Da_insert(&nums, 7, 0);
Da_foreach(n, nums) printf("%d\n", *n);
Da_destroy(&nums);
| Macro | Description |
|---|---|
Da_append(da, e) |
Add e at the end, doubling the capacity when full |
Da_insert(da, e, i) |
Insert e at index i |
Da_remove(da, i), Da_remove_last(da) |
Remove an element |
Da_index(elem_ptr, da) |
Index of an element given a pointer to it |
Da_foreach(i, da) |
Iterate with i pointing to each element |
Da_dup(da) |
Copy of the struct and its buffer |
Da_destroy(da) |
Free the buffer and reset the array |
Ss(type) is the same structure used as a stack, with
Ss_push, Ss_pop and Ss_top.
Commands
Command is a Da(char *).
DECLARE_COMMAND_RUN() defines
Command_run(cmd), which forks, execvps the
command and waits for it. It returns the exit status, 127 if exec
failed, or 1 if the fork failed or the child was killed by a signal.
DECLARE_COMMAND_RUN()
Command cmd = { 0 };
Command_add(&cmd, "make", "-j8");
int status = Command_run(cmd);
Command_destroy(&cmd);
Helpers
| Macro | Description |
|---|---|
Let |
__auto_type in C, auto in C++ |
Typeof(x) |
__typeof__ / typeof |
Todo(fmt, ...) |
Print file, line and message, then abort() |
Min(a, b), Max(a, b) |
Evaluate each argument once |
Memdup(x), Memzero(x) |
Heap copy / zero an object |
KB(x), MB(x), GB(x),
TB(x) |
Sizes as size_t |
Bf_set, Bf_clr, Bf_has |
Bit flags |
Tostr(x) |
Stringify |
Used by
- pm.
See also
- Source code (public domain)
- C libraries