読者です 読者をやめる 読者になる 読者になる

テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

(macro-of-inline report) ブロック内のnon-void関数コールを括りだすアルゴリズムの検討

これは, 以下の記事の続きである.

(macro-of-inline report) non-void関数のvoid化における呼び出し元での引数評価順序について - テストステ論

ブロック内にあるnon-voidな関数コールを, 以下のように変形して, voidな関数コールに帰着させる必要がある.

f(g());

=>

void_g(&b);
void_f(&a, b);

私は, 変形を3段階に分けようと思っている.

  1. ret = f(...) に変形する.
  2. そいつらから呼び出しのネストがあった場合は括りだす.
  3. そいつらをすべて, void呼び出し形に変形する.

3は明らかだ. 2は実装がちょっと難しいが, 出来るは出来る. よって問題は1だ. ここではネストも考える必要がある. (2は以前に述べたように, 一番最初に見つかったマクロ化対象の関数を括りだすを繰り返すというのがシンプル)

私はこう考える.

  • ブロック内を順に走査して(ブロック内のindexを追い続ける), その中でマッチした形の変形を行う.
  • マッチさせる形は, f(), (void) f(), return f()の3つとする. (別にマッチが漏れても性能が悪くなるだけでご動作はしない)

擬似コードでいうと,

for i, line in block:
  match line:
    f() -> replace i, "rand = f()" & insert 0, "T rand"
    (void) f() -> same above
    return f() -> replace i, "return $rand" & insert i, "rand = f()" & insert 0, "T rand"

2番目のが必要なのは, f()が値を返すのにその値を使わない場合は, (void)をつけなさいというコーディング規約もあるから.

上の例でいうと,

T a;
a = f(g());
T a;
T b;
b = g();
a = f(b);
T a;
T b;
void_g(&b);
void_f(&a, b);

という手順で変形する.