テストステ論

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

(macro-of-inline report) 非void返しへの適用案

macro-of-inlineは現在, void返しの関数しかマクロ化をサポートしない. それはテクニカルな理由からである. 正直にreturn non-voidな関数をマクロ化しようとすれば, expression statement(GCC拡張)が必要になるだろうというのが私の結論である.

さきほど, シャワーを浴びていたらふと思ったのだが, non-voidな関数をvoidな関数に変更してしまえば, 「一応」マクロ変換が出来るようにはなるではないか?

この自明なアイデアは,

int f() { return 0; }

int x = f();

void f(int *retval) { *retval = 0; }

int x;
f(&x);

に事前に書換えてから, 今までの処理を開始するということだ. もちろん, マクロ化をしないのであればこの書換は性能を落とす(あともしかしたらプログラムの論理自体を書換える可能性があるような気がする). しかし, マクロ化がどうしても必要ということであれば, やることも一つの選択肢となる.

設計はどうだろうか. この事前書換処理は, inlineの関数を探すだとか, 返り値はなんだとか, もともとある処理に共通の実装が多い. 従ってもしかしたら, 今までの処理の中に組み込む形になるかも知れないし, あるいは, 今までの処理のちょうど前に実行されるかも知れないし, 全く独立のプログラムになるかも知れない. やってみないと分からないだろうが, 直感では, 完全に独立ということにはならないだろうと思う. macro-of-inlineのオプションとして実装されるのがあるべき姿だろう.

アルゴリズムは, よくわからない. 第一感は以下である.

  1. inline non-voidな関数について, return文を代入文に置き換えて, ラストに引数retvalを付け加える(もともとの返り値のポインタ型). 関数定義の方を書換えるのは, 出来るように思うし, 実装もきれいだろう.
  2. 問題は呼び出し側の書換. それらの関数を呼び出している箇所をすべて書換える. 以下で例を示す.

例えば, 馬鹿正直に

int x;

x = f();

などと書いてくれていれば, これは簡単そうである. もしかしたら, この代入文を単にf(&x)と書き換えれば終わるかも知れない. int x = f()と書いてあっても問題の難易度が飛躍的に上がるわけではなさそうだ. 問題は以下のようなケース.

g(f());

これは厳しい.

int x;

f(&x);
g(ret)

のように書換える必要があるが, 難易度はそこそこ上がる. さらに

int y = g(f());

のような形でf, gがinline化される時はどうすればいいのだろうか?

int x;
int y;
f(&x);
g(x, &y);

のようになるべきだと思うが, これは再帰の形をしているので非常にだるい(例: h(g(f()))のような形を考えよう). また, 呼び出しごとに変数が増えるというのも, なかなか鬱陶しいものがある(ふつうのコンパイラならば性能に変化はなさそうだが, 我々が相手にしているのは糞コンパイラなのだ).

と悩んでいくと, 結構深い気がしてくる. 実装は数百行は行くだろうという感じがしてくる.

(直感的には, そのブロックにおいて, いくつのマクロ化されるinline関数が呼ばれていて, それぞれの返り値が何か, それぞれがどういう順序で呼ばれるかということは分かる. 従って, ブロックを訪れるごとにそれらに適当な名前をつけて変数を用意して, あとは, ネストの深いところにあるものから呼び出しを列挙していけばよい. のだが, 結構鬱陶しいし本当にそれでいいかも定かではない)


で, どうするか. とりあえずもう少し検討はするが, たぶんやらないだろう. やるにしても, まず現状で1.0としてpypiに登録する. その上で1.1への拡張としてやるというのが正しい計画だと思う.