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

テストステ論

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

(macro-of-inline report) マクロ化対象関数の洗い出しアルゴリズム

今後また落とし穴が見つかるかも知れないので, 設計はシンプルな方がいい.

私の頭の中では以下の手順で, マクロ化が出来るということになっている.

  1. マクロ化対象関数リストを洗い出す
  2. non-voidな関数について, 呼び出しもとの変形をする
  3. non-voidな関数について, 定義を変形して, voidとする.
  4. voidな関数について, 呼び出しもとを変形する.
  5. voidな関数について, 定義を変形してmacroとする.

例えば, 3や5の実行中に, 「やっぱマクロ化出来ないわ」と判定して, マクロ化対象関数が削減されることは決してないと仮定している.

理由は, 「それで大丈夫そうと感じる」以外に, 処理の途中でマクロ化出来ないことを気づくことが明らかに超難しいからである. 無理なものは最初から諦めて割り切る方が工学的思考だ.

呼び出しもとから変形する(i.e. 2->3, 4->5の順である)のは, 呼び出しもとの変形に, 定義を使う可能性があるからである. 対して, 定義の変形は, たぶん, 「どんな形にも対応出来る」ほど一般的であるから, 仮に, その関数自体が呼び出しもとの変形を受けたとしても, 変形出来なくなることはない. 定義の方から書換えて, 呼び出し元を書き換える方が直感的であるが, マクロ化については逆の方が良いと判断した.

この記事では, 1について考察する.

現在, どういう関数はマクロ化出来るかと考えた時,

最後のものは,

if (cond) {}
while (cond) {}

など, cond(<expression)の中で呼ばれないということである. C言語は, expressionの下にstatementがあることを拒む.

私の考えでは, expressionの中でコールされていないものは全部マクロ化出来る. 私は事前処理として, 以下のような変形を施している.

if (cond)
  x = 1;
if (cond) {
  x = 1;
}

このように, statement部分は必ずcompoundであるように前処理をする(何をcompound化すべきかは, BNFを見れば分かる).

すると, すべてのコードは, 以下のどちらかに分類されることになる.

  • expression
  • expression以外

これで全網羅を書くことが簡単になる.

私が実装する, マクロ化対象関数の洗い出しアルゴリズムは極めてシンプルである.

  1. ファイル中の全関数呼び出し名を列挙する.
  2. compoundでのみ呼ばれている関数呼び出しを全列挙する.
  3. 1-2に入ってるものはマクロ化出来ない.

ここで注目するのは, 2で「関数の引数や定義内の変数定義によるシャドウイングを全く考慮しない」ということである. これを考慮するには, compoundに入るごとに変数名の集合を追いかけ続ければいい. しかし私はこれを嫌うことにした. 理由は, 上の1, 2という単純な列挙の差分をとった方が堅牢になるからである.

これは考え方としてはこのようなことである. ある関数呼び出しからその何らかの名前を計算する関数をfとすると, 1 = map f $ ([compound] + [expression]) であり, 2 = map $ [compound] である. 1は, map f [compound] + map f [expression]であるから, この差分は明らかに3 = map f [expression]となる. もしここに, 変数集合を追い続けるなどといった副作用が含まれたら, この性質は保証されなくなる可能性が高い. 従って, 変数集合は追い続けるべきではない.

かくして, set $ map f [expression] に出てきた名前はマクロ化禁止ということになる. ここでこの集合にとって重要なのは, 「マクロ化禁止の関数をすべて含むこと」である. つまり, マクロ化可能な関数を偶然に含んでしまった結果, その関数がマクロ化されなくなることは動作の誤りにはならない. もともと, expressionの中で呼ばれている関数はマクロ化しないことが方針であるから, 関数fが, 外部で定義された関数呼び出しについて正しくその名前を返す関数であればそれでいい(その他の場合は何を返してもいい. 環境に依存しなければ). 今fは明らかにその性質を満たせるから, 上記のアルゴリズムは正しく動作する. また, 上記誤りは恣意的な場合でない限り発生しないレベルであるため, 現実的にはこのアルゴリズムは, 理想の場合と同等の結果を計算する.

という感じで拝承出来る.

文章でまとめておくと, 考えの整理になるしあとで参照も出来る. 徳が深い.

マクロ化は読んでいくとなかなか深い...


実装終了

*1:staticをインライン化に含むオプションは危険かも知れない. このオプションは消すべきかも