Si vous souhaitez mettre en place une boucle while, vous aurez besoin d'utiliser la récursivité dans le préprocesseur. La meilleure façon de faire de la récursivité est d'utiliser un différé d'expression. Un différé d'expression est une expression qui nécessite des analyses plus expansion:
#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
Pourquoi est-ce important? Ainsi lorsqu'une macro est scanné et se développe, il crée une désactivation de contexte. Cette désactivation est le contexte qui va provoquer un jeton, qui fait référence à l'expansion de la macro, pour être peinte en bleu. Ainsi, une fois peint en bleu, la macro va plus s'étendre. C'est pourquoi les macros ne développent pas de manière récursive. Cependant, la désactivation de contexte n'existe que pendant une analyse, par le report d'un agrandissement, nous pouvons empêcher nos macros de devenir peint en bleu. Nous allons juste besoin d'appliquer des analyses plus à l'expression. Nous pouvons le faire à l'aide de cette EVAL
macro:
#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__
Ensuite, nous définissons certains opérateurs pour faire un peu de logique(comme si, etc):
#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))
Maintenant, avec tous ces macros, nous pouvons écrire un récursif WHILE
macro. Nous utilisons un WHILE_INDIRECT
macro pour renvoyer à lui-même de manière récursive. Cela empêche la macro d'être peint en bleu, car il s'étendra sur un autre scan(et en utilisant un autre désactivation de contexte). L' WHILE
macro prend un prédicat macro, un opérateur de macro, et un état(ce qui est le variadic arguments). Il maintient l'application de cet opérateur de macro à l'état jusqu'à ce que le prédicat de la macro renvoie la valeur false(0).
#define WHILE(pred, op, ...) \
IF(pred(__VA_ARGS__)) \
( \
DEFER(WHILE_INDIRECT) () \
( \
pred, op, op(__VA_ARGS__) \
), \
__VA_ARGS__ \
)
#define WHILE_INDIRECT() WHILE
Pour les besoins de la démonstration, nous allons créer un prédicat qui vérifie si le nombre d'arguments sont de 1:
#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__)))
Ensuite, nous créons un opérateur, ce qui, nous allons juste concat deux jetons. Nous avons également créer un opérateur final(appelé M
) qui traitera le résultat final:
#define OP(x, y, ...) CAT(x, y), __VA_ARGS__
#define M(...) CAT(__VA_ARGS__)
Ensuite, à l'aide de l' WHILE
macro:
M(EVAL(WHILE(PRED, OP, x, y, z))) //Expands to xyz
Bien sûr, tout type de prédicat ou l'opérateur peut être transmis.