C プリプロセッサで while ループを書くにはどうすればいいですか? 質問する

C プリプロセッサで while ループを書くにはどうすればいいですか? 質問する

私は教育的/ハッキング的な観点からこの質問をしています (私は実際にこのようなコードを書きたいとは思いません)。

whileループを実装するには、プリプロセッサ ディレクティブ。マクロは再帰的に展開できないことは理解していますが、これはどのように実現できるのでしょうか?

ベストアンサー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

もちろん、任意の種類の述語または演算子を渡すこともできます。

おすすめ記事