テストステ論

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

(macro-of-inline) inline関数を関数ポインタとして渡すのは標準ではない

ガード節がある関数に対応するために, 関数ブロックの末尾にlabelを仕込み, returnをgotoで置換する実装を先ほど行った. 今私が行っているのは, 関数呼び出しに対してlabel用のランダムな名前を渡す実装だ.

これが骨だ. なんと, FuncCallノードを探索すると, 引数として渡されてきた関数ポインタの呼び出しまでヒットしてしまう.

まず, 問題を小さくするために, マクロ化対象であるinline関数が関数ポインタとして渡すことが不可能なことを実証する.

http://melpon.org/wandbox/permlink/EGxxaVWxYzsFi6S7

#include <stdio.h>

inline void fun(void) { puts("fun"); }

void f(void (*g)(void))
{
    g();
}

int main()
{
    f(fun);
    return 0;
}

以下がエラーメッセージ. funをinline以外にすると, コンパイルを通る. gccのg++でコンパイルするとコンパイル出来るので, 標準のC以上のどこかで拡張されてるのだと思う.

/tmp/cc8Z9sti.o: In function `main':
prog.c:(.text+0x19): undefined reference to `fun'
collect2: error: ld returned 1 exit status

この実験の結果より, 以下の方法によって関数ポインタ呼び出しを排除することが出来る. たぶんもっともシンプルな方式だろう.

  1. 関数ブロック内において, 引数として渡されてきた関数ポインタもランダムな名前によってrenameする(#).
  2. FuncCallで検索して, 名前が明らかにランダムな名前を持ってるものは排除する.

2について, もともと, 「人間がつけた名前はランダムな名前と衝突しない」ということを前提にしているので, もしこれが許されないのであれば前提が破綻するので依存していい.

あとは, 1の(#)部分, renameしていないのがただ意味ないからなのか, 他に意図があるのかが分からないので, こいつを確認する必要がある.


(追記)

inlineのみである場合にエラーとなり, static inlineの場合はコンパイルが通るようである. この点も考慮する必要がある.