Jump to content

cum.h

From Hugo's wiki

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.

Note It needs at least -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 to M so 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.
  • EVAL forces 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

See also