Wenn Sie eine While-Schleife implementieren möchten, müssen Sie die Rekursion im Präprozessor verwenden. Der einfachste Weg, eine Rekursion durchzuführen, ist die Verwendung eines zurückgestellten Ausdrucks. Ein verzögerter Ausdruck ist ein Ausdruck, der mehr Scans erfordert, um vollständig erweitert zu werden:
#define EMPTY()
#define DEFER(id) id EMPTY()
#define OBSTRUCT(id) id DEFER(EMPTY)()
#define EXPAND(...) __VA_ARGS__
#define A() 123
A() // Expands to 123
DEFER(A)() // Expands to A () because it requires one more scan to fully expand
EXPAND(DEFER(A)()) // Expands to 123, because the EXPAND macro forces another scan
Warum ist das wichtig? Nun, wenn ein Makro gescannt und erweitert wird, erstellt es einen deaktivierenden Kontext. Dieser deaktivierende Kontext bewirkt, dass ein Token, das sich auf das aktuell expandierende Makro bezieht, blau gefärbt wird. Sobald es also blau gefärbt ist, wird das Makro nicht mehr erweitert. Aus diesem Grund werden Makros nicht rekursiv erweitert. Ein deaktivierender Kontext existiert jedoch nur während eines Scans, sodass wir durch Zurückstellen einer Erweiterung verhindern können, dass unsere Makros blau gefärbt werden. Wir müssen nur mehr Scans auf den Ausdruck anwenden. Das können wir damit machen EVAL
Makro:
#define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
#define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__)))
#define EVAL5(...) __VA_ARGS__
Als Nächstes definieren wir einige Operatoren, um eine Logik auszuführen (z. B. if usw.):
#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__
#define CHECK_N(x, n, ...) n
#define CHECK(...) CHECK_N(__VA_ARGS__, 0,)
#define NOT(x) CHECK(PRIMITIVE_CAT(NOT_, x))
#define NOT_0 ~, 1,
#define COMPL(b) PRIMITIVE_CAT(COMPL_, b)
#define COMPL_0 1
#define COMPL_1 0
#define BOOL(x) COMPL(NOT(x))
#define IIF(c) PRIMITIVE_CAT(IIF_, c)
#define IIF_0(t, ...) __VA_ARGS__
#define IIF_1(t, ...) t
#define IF(c) IIF(BOOL(c))
Jetzt können wir mit all diesen Makros eine rekursive schreiben WHILE
Makro. Wir benutzen ein WHILE_INDIRECT
Makro rekursiv auf sich selbst zurückgreifen. Dadurch wird verhindert, dass das Makro blau gezeichnet wird, da es bei einem anderen Scan erweitert wird (und einen anderen Deaktivierungskontext verwendet). Das WHILE
Makro nimmt ein Prädikat-Makro, ein Operator-Makro und einen Zustand (das sind die variadischen Argumente). Es wendet dieses Operator-Makro so lange auf den Zustand an, bis das Prädikat-Makro „false“ (was 0 ist) zurückgibt.
#define WHILE(pred, op, ...) \
IF(pred(__VA_ARGS__)) \
( \
OBSTRUCT(WHILE_INDIRECT) () \
( \
pred, op, op(__VA_ARGS__) \
), \
__VA_ARGS__ \
)
#define WHILE_INDIRECT() WHILE
Zu Demonstrationszwecken erstellen wir nur ein Prädikat, das prüft, ob die Anzahl der Argumente 1 ist:
#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define NARGS(...) NARGS_SEQ(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)
#define IS_1(x) CHECK(PRIMITIVE_CAT(IS_1_, x))
#define IS_1_1 ~, 1,
#define PRED(x, ...) COMPL(IS_1(NARGS(__VA_ARGS__)))
Als nächstes erstellen wir einen Operator, mit dem wir einfach zwei Token verketten. Wir erstellen auch einen letzten Operator (genannt M
), die die endgültige Ausgabe verarbeiten:
#define OP(x, y, ...) CAT(x, y), __VA_ARGS__
#define M(...) CAT(__VA_ARGS__)
Dann mit der WHILE
Makro:
M(EVAL(WHILE(PRED, OP, x, y, z))) //Expands to xyz
Natürlich kann ihm jede Art von Prädikat oder Operator übergeben werden.
Nun, nicht dass es eine While-Schleife ist, sondern eine Counter-Schleife, trotzdem ist die Schleife in sauberem CPP möglich (keine Templates und kein C++)
#ifdef pad_always
#define pad(p,f) p##0
#else
#define pad0(p,not_used) p
#define pad1(p,not_used) p##0
#define pad(p,f) pad##f(p,)
#endif
// f - padding flag
// p - prefix so far
// a,b,c - digits
// x - action to invoke
#define n0(p,x)
#define n1(p,x) x(p##1)
#define n2(p,x) n1(p,x) x(p##2)
#define n3(p,x) n2(p,x) x(p##3)
#define n4(p,x) n3(p,x) x(p##4)
#define n5(p,x) n4(p,x) x(p##5)
#define n6(p,x) n5(p,x) x(p##6)
#define n7(p,x) n6(p,x) x(p##7)
#define n8(p,x) n7(p,x) x(p##8)
#define n9(p,x) n8(p,x) x(p##9)
#define n00(f,p,a,x) n##a(pad(p,f),x)
#define n10(f,p,a,x) n00(f,p,9,x) x(p##10) n##a(p##1,x)
#define n20(f,p,a,x) n10(f,p,9,x) x(p##20) n##a(p##2,x)
#define n30(f,p,a,x) n20(f,p,9,x) x(p##30) n##a(p##3,x)
#define n40(f,p,a,x) n30(f,p,9,x) x(p##40) n##a(p##4,x)
#define n50(f,p,a,x) n40(f,p,9,x) x(p##50) n##a(p##5,x)
#define n60(f,p,a,x) n50(f,p,9,x) x(p##60) n##a(p##6,x)
#define n70(f,p,a,x) n60(f,p,9,x) x(p##70) n##a(p##7,x)
#define n80(f,p,a,x) n70(f,p,9,x) x(p##80) n##a(p##8,x)
#define n90(f,p,a,x) n80(f,p,9,x) x(p##90) n##a(p##9,x)
#define n000(f,p,a,b,x) n##a##0(f,pad(p,f),b,x)
#define n100(f,p,a,b,x) n000(f,p,9,9,x) x(p##100) n##a##0(1,p##1,b,x)
#define n200(f,p,a,b,x) n100(f,p,9,9,x) x(p##200) n##a##0(1,p##2,b,x)
#define n300(f,p,a,b,x) n200(f,p,9,9,x) x(p##300) n##a##0(1,p##3,b,x)
#define n400(f,p,a,b,x) n300(f,p,9,9,x) x(p##400) n##a##0(1,p##4,b,x)
#define n500(f,p,a,b,x) n400(f,p,9,9,x) x(p##500) n##a##0(1,p##5,b,x)
#define n600(f,p,a,b,x) n500(f,p,9,9,x) x(p##600) n##a##0(1,p##6,b,x)
#define n700(f,p,a,b,x) n600(f,p,9,9,x) x(p##700) n##a##0(1,p##7,b,x)
#define n800(f,p,a,b,x) n700(f,p,9,9,x) x(p##800) n##a##0(1,p##8,b,x)
#define n900(f,p,a,b,x) n800(f,p,9,9,x) x(p##900) n##a##0(1,p##9,b,x)
#define n0000(f,p,a,b,c,x) n##a##00(f,pad(p,f),b,c,x)
#define n1000(f,p,a,b,c,x) n0000(f,p,9,9,9,x) x(p##1000) n##a##00(1,p##1,b,c,x)
#define n2000(f,p,a,b,c,x) n1000(f,p,9,9,9,x) x(p##2000) n##a##00(1,p##2,b,c,x)
#define n3000(f,p,a,b,c,x) n2000(f,p,9,9,9,x) x(p##3000) n##a##00(1,p##3,b,c,x)
#define n4000(f,p,a,b,c,x) n3000(f,p,9,9,9,x) x(p##4000) n##a##00(1,p##4,b,c,x)
#define n5000(f,p,a,b,c,x) n4000(f,p,9,9,9,x) x(p##5000) n##a##00(1,p##5,b,c,x)
#define n6000(f,p,a,b,c,x) n5000(f,p,9,9,9,x) x(p##6000) n##a##00(1,p##6,b,c,x)
#define n7000(f,p,a,b,c,x) n6000(f,p,9,9,9,x) x(p##7000) n##a##00(1,p##7,b,c,x)
#define n8000(f,p,a,b,c,x) n7000(f,p,9,9,9,x) x(p##8000) n##a##00(1,p##8,b,c,x)
#define n9000(f,p,a,b,c,x) n8000(f,p,9,9,9,x) x(p##9000) n##a##00(1,p##9,b,c,x)
#define n00000(f,p,a,b,c,d,x) n##a##000(f,pad(p,f),b,c,d,x)
#define n10000(f,p,a,b,c,d,x) n00000(f,p,9,9,9,9,x) x(p##10000) n##a##000(1,p##1,b,c,d,x)
#define n20000(f,p,a,b,c,d,x) n10000(f,p,9,9,9,9,x) x(p##20000) n##a##000(1,p##2,b,c,d,x)
#define n30000(f,p,a,b,c,d,x) n20000(f,p,9,9,9,9,x) x(p##30000) n##a##000(1,p##3,b,c,d,x)
#define n40000(f,p,a,b,c,d,x) n30000(f,p,9,9,9,9,x) x(p##40000) n##a##000(1,p##4,b,c,d,x)
#define n50000(f,p,a,b,c,d,x) n40000(f,p,9,9,9,9,x) x(p##50000) n##a##000(1,p##5,b,c,d,x)
#define n60000(f,p,a,b,c,d,x) n50000(f,p,9,9,9,9,x) x(p##60000) n##a##000(1,p##6,b,c,d,x)
#define n70000(f,p,a,b,c,d,x) n60000(f,p,9,9,9,9,x) x(p##70000) n##a##000(1,p##7,b,c,d,x)
#define n80000(f,p,a,b,c,d,x) n70000(f,p,9,9,9,9,x) x(p##80000) n##a##000(1,p##8,b,c,d,x)
#define n90000(f,p,a,b,c,d,x) n80000(f,p,9,9,9,9,x) x(p##90000) n##a##000(1,p##9,b,c,d,x)
#define cycle5(c1,c2,c3,c4,c5,x) n##c1##0000(0,,c2,c3,c4,c5,x)
#define cycle4(c1,c2,c3,c4,x) n##c1##000(0,,c2,c3,c4,x)
#define cycle3(c1,c2,c3,x) n##c1##00(0,,c2,c3,x)
#define cycle2(c1,c2,x) n##c1##0(0,,c2,x)
#define cycle1(c1,x) n##c1(,x)
#define concat(a,b,c) a##b##c
#define ck(arg) a[concat(,arg,-1)]++;
#define SIZEOF(x) (sizeof(x) / sizeof((x)[0]))
void check5(void)
{
int i, a[32769];
for (i = 0; i < SIZEOF(a); i++) a[i]=0;
cycle5(3,2,7,6,9,ck);
for (i = 0; i < SIZEOF(a); i++) if (a[i] != 1) printf("5: [%d] = %d\n", i+1, a[i]);
}