私は教育的/ハッキング的な観点からこの質問をしています (私は実際にこのようなコードを書きたいとは思いません)。
whileループを実装するには、Cプリプロセッサ ディレクティブ。マクロは再帰的に展開できないことは理解していますが、これはどのように実現できるのでしょうか?
ベストアンサー1
while ループを実装する場合は、プリプロセッサで再帰を使用する必要があります。再帰を実行する最も簡単な方法は、遅延式を使用することです。遅延式とは、完全に展開するためにより多くのスキャンを必要とする式です。
#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
なぜこれが重要なのでしょうか。マクロがスキャンされて展開されると、無効化コンテキストが作成されます。この無効化コンテキストにより、現在展開中のマクロを参照するトークンが青く塗られます。したがって、青く塗られると、マクロはそれ以上展開されなくなります。マクロが再帰的に展開されないのは、このためです。ただし、無効化コンテキストは 1 回のスキャン中にのみ存在するため、展開を延期することで、マクロが青く塗られるのを防ぐことができます。式にさらにスキャンを適用する必要があります。次のEVAL
マクロを使用してこれを行うことができます。
#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__
次に、何らかのロジックを実行するための演算子をいくつか定義します (if など)。
#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))
これらすべてのマクロを使用して、再帰WHILE
マクロを記述できます。マクロを使用してWHILE_INDIRECT
、再帰的にマクロ自体を参照します。これにより、マクロは別のスキャンで展開され (別の無効化コンテキストを使用する)、青く塗りつぶされることがなくなります。マクロWHILE
は、述語マクロ、演算子マクロ、および状態 (可変引数) を受け取ります。述語マクロが false (0) を返すまで、この演算子マクロを状態に適用し続けます。
#define WHILE(pred, op, ...) \
IF(pred(__VA_ARGS__)) \
( \
OBSTRUCT(WHILE_INDIRECT) () \
( \
pred, op, op(__VA_ARGS__) \
), \
__VA_ARGS__ \
)
#define WHILE_INDIRECT() WHILE
デモンストレーションの目的で、引数の数が 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__)))
M
次に、2 つのトークンを連結する演算子を作成します。また、最終出力を処理する最終演算子 ( ) も作成します。
#define OP(x, y, ...) CAT(x, y), __VA_ARGS__
#define M(...) CAT(__VA_ARGS__)
次にWHILE
マクロを使用します:
M(EVAL(WHILE(PRED, OP, x, y, z))) //Expands to xyz
もちろん、任意の種類の述語または演算子を渡すこともできます。