C99のva_copy
可変引数APIへの追加は、チューリング完全性への裏口を私たちに与えるかもしれません。最初に引数を受け取った関数以外の関数で可変引数リストを複数回反復処理することが可能になるva_args
ため、ポインターなしのポインターを実装するために使用できます。
もちろん、可変引数APIの実際の実装にはおそらくどこかにポインターがありますが、抽象マシンでは、代わりにマジックを使用して実装できます。
以下に、任意の遷移ルールを使用した2スタックプッシュダウンオートマトンを実装するデモを示します。
#include <stdarg.h>
typedef struct { va_list va; } wrapped_stack; // Struct wrapper needed if va_list is an array type.
#define NUM_SYMBOLS /* ... */
#define NUM_STATES /* ... */
typedef enum { NOP, POP1, POP2, PUSH1, PUSH2 } operation_type;
typedef struct { int next_state; operation_type optype; int opsymbol; } transition;
transition transition_table[NUM_STATES][NUM_SYMBOLS][NUM_SYMBOLS] = { /* ... */ };
void step(int state, va_list stack1, va_list stack2);
void push1(va_list stack2, int next_state, ...) {
va_list stack1;
va_start(stack1, next_state);
step(next_state, stack1, stack2);
}
void push2(va_list stack1, int next_state, ...) {
va_list stack2;
va_start(stack2, next_state);
step(next_state, stack1, stack2);
}
void step(int state, va_list stack1, va_list stack2) {
va_list stack1_copy, stack2_copy;
va_copy(stack1_copy, stack1); va_copy(stack2_copy, stack2);
int symbol1 = va_arg(stack1_copy, int), symbol2 = va_arg(stack2_copy, int);
transition tr = transition_table[state][symbol1][symbol2];
wrapped_stack ws;
switch(tr.optype) {
case NOP: step(tr.next_state, stack1, stack2);
// Note: attempting to pop the stack's bottom value results in undefined behavior.
case POP1: ws = va_arg(stack1_copy, wrapped_stack); step(tr.next_state, ws.va, stack2);
case POP2: ws = va_arg(stack2_copy, wrapped_stack); step(tr.next_state, stack1, ws.va);
case PUSH1: va_copy(ws.va, stack1); push1(stack2, tr.next_state, tr.opsymbol, ws);
case PUSH2: va_copy(ws.va, stack2); push2(stack1, tr.next_state, tr.opsymbol, ws);
}
}
void start_helper1(va_list stack1, int dummy, ...) {
va_list stack2;
va_start(stack2, dummy);
step(0, stack1, stack2);
}
void start_helper0(int dummy, ...) {
va_list stack1;
va_start(stack1, dummy);
start_helper1(stack1, 0, 0);
}
// Begin execution in state 0 with each stack initialized to {0}
void start() {
start_helper0(0, 0);
}
注:場合 va_list
が配列型の、実際には関数への非表示ポインターパラメーターがあります。したがって、おそらくすべてのva_list
引数のタイプをに変更した方がよいでしょうwrapped_stack
。