JustUI/include/justui/p/preproc.h

64 lines
2.3 KiB
C

//---
// JustUI.util.preproc: Preprocessor utilities
//---
#ifndef _J_UTIL_PREPROC
#define _J_UTIL_PREPROC
/* J_REPEAT: Dispatch a partially-evaluated macro call on each arg in a list
The call `J_REPEAT(X, C, V1, ..., VN)` where `C = (C1, ..., CM)` (with the
parentheses) is equivalent to the series of calls
```
X(C1, ..., CM, V1)
X(C1, ..., CM, V2)
...
X(C1, ..., CM, VN)
```
and is used to iterate on lists. There is a size limit of N≤8. */
#define J_REPEAT1(X, C, V1, ...) \
J_CALL(X, C, V1) __VA_OPT__(J_REPEAT2(X, C, __VA_ARGS__))
#define J_REPEAT2(X, C, V2, ...) \
J_CALL(X, C, V2) __VA_OPT__(J_REPEAT3(X, C, __VA_ARGS__))
#define J_REPEAT3(X, C, V3, ...) \
J_CALL(X, C, V3) __VA_OPT__(J_REPEAT4(X, C, __VA_ARGS__))
#define J_REPEAT4(X, C, V4, ...) \
J_CALL(X, C, V4) __VA_OPT__(J_REPEAT5(X, C, __VA_ARGS__))
#define J_REPEAT5(X, C, V5, ...) \
J_CALL(X, C, V5) __VA_OPT__(J_REPEAT6(X, C, __VA_ARGS__))
#define J_REPEAT6(X, C, V6, ...) \
J_CALL(X, C, V6) __VA_OPT__(J_REPEAT7(X, C, __VA_ARGS__))
#define J_REPEAT7(X, C, V7, ...) \
J_CALL(X, C, V7) __VA_OPT__(J_REPEAT8(X, C, __VA_ARGS__))
#define J_REPEAT8(X, C, V8, ...) \
({ __VA_OPT__(_Static_assert(0, \
"J_REPEAT: too many macro arguments (maximum 8)");) \
J_CALL(X, C, V8); })
#define J_REPEAT(X, C, ...) __VA_OPT__(J_REPEAT1(X, C, __VA_ARGS__))
/* J_CALL: Perform a call to a partially evaluated macro
The call `J_CALL(X, C, A1, ..., AN)` where `C = (C1, ..., CM)` (with the
parentheses) reduces to `X(C1, ..., CM, A1, ..., AN)`, i.e. it calls the
already-partially-applied `X(C)` with further arguments. Both M=0 (`C=()`)
and N=0 (no variadic arguments) are allowed.
The main difficulty is "unfolding" `C` into the arguments of `X`. This
problem is dealt with by absorbing the parentheses into an ID-function macro
call:
```
J_ID C
~> J_ID (C1, ..., CM)
~> , C1, ..., CM
```
From there, the only subtletly is gobbling the commas. We gobble the comma
before `J_ID C` if M=0 and the comma before the other arguments if N=0. This
requires a few expansion stages. */
#define J_ID(...) __VA_OPT__(,) __VA_ARGS__
#define J_CALL3(X, ...) X(__VA_ARGS__)
#define J_CALL2(...) J_CALL3(__VA_ARGS__)
#define J_CALL(X, C, ...) J_CALL2(X J_ID C, ##__VA_ARGS__)
#endif /* _J_UTIL_PREPROC */