テストステ論

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

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

昨日トータルで12時間くらいかけて, non-voidな関数をvoidな関数に書換える処理を一気に書きました. 今朝, 複雑な場合についていくらかバグを見つけて, 修正して, まぁまぁ動いてるかのように見えます.

以下で, f1-4がすべてinline int fk(int n)の型を持っていた場合, コードはどのように変換されるでしょうか?

g(f1(f2(0)), f3(f4(0)));

今の実装は, たぶん,

a = f2(0);
b = f1(a);
c = f4(0);
d = f3(c);
g(b, d);

と展開されてから, f2(0) -> f2(0, &a)のように変換されて終了します. つまり, 評価は, f2, f1, f4, f3の順で行われます. 関数ブロックをdepth firstで辿っていって, 一番はじめに見つかったinline Tの関数をその行にくくりだすを繰り返すという単純なしくみを採用しています.

しかし, C言語の標準としては, 引数の評価順はどうなっているか不問ということです. つまり, f2の中の副作用をf3が使うということは「許されない」ということです. 守らないといけないのは, f1, f3 < g, f2 < f1, f4 < f3 (a < bはaがbより先に呼ばれるの意味)だけです.

引数の評価順序 - Togetterまとめ

このTogetterを見れば分かるように, 引数が先頭から呼ばれると勘違いしてる人は多いです. なのでもしかしたら, それに依存したプログラムが存在する可能性があります. そういうプログラムは無視してもいいと思いますが, macro-of-inlineは, 変換後のコードに可読性が全くないので, 問題が起きた時にコードレベルで対処することはほぼ不可能です. なので, 出来るだけ使い手の期待をくんだ設計にしようということで, 今のアルゴリズムを採用しました. 関数呼び出しを辿っていく処理に無駄があり, 性能はよくないと思いますが, 関数が超ネストしていることなどというのは現実的にはそんなにない(コード上そう見えても, それは可読性をあげるためのマクロだったりすることが現実的には多いです)ため, 現実的にはどういうアルゴリズムを採用しても実行時間に差はなかろうと判断しました. コードもシンプルなため良いと思います.


まだバグはあると思いますが, とりあえず平和な週末を過ごしたいです.

macro-of-inlineに対するコントリビューション待ってます.

akiradeveloper/macro-of-inline · GitHub