「試験をしているんだけど、実行中に関数の戻り値を変える方法はないかな?」
と相談を受けました。
C言語で単体テストをする場合、システムコールやライブラリ関数はダミーの関数を用意しておいて自作部分の実装をテストすることはよくありますが、実行中にこのダミー関数の振舞いを変えたいという趣旨です。*1
単純にダミー関数の戻り値をグローバル変数にしてしまえば問題はありません。
ただしテストコードが多いと呼び出し元のテストコードが少し複雑になりそうです。
少し考えてみましたが、
DLL,共有ライブラリならできるのでは?
というのが私の思いついた答えでした。
linuxだとdlopen,dlsym,dlcloseを使って、ダミー関数を共有ライブラリにしておけば実際に呼び出される関数を変更することができます。
ということでそれっぽいコードを書いてみました。
試してみたコード
呼び出し元のソースコード(main.c)
#include <stdio.h> #include <dlfcn.h> #include <unistd.h> int add(int a, int b) { int result; void *handle_a = dlopen("./liba.so", RTLD_LAZY); int(* add_handler)(int, int) = dlsym( handle_a, __FUNCTION__ ); result = add_handler(a, b); dlclose(handle_a); return result; } int main(int argc, char **argv) { int result = 0; while(1) { result = add(1,2); sleep(1); printf("%d\n", result); } return 0; }
ダミー関数の実装(add.c)
int add(int a, int b) { return a + b; }
ビルドと実行
$ gcc -shared add.c -o libadd.so
$ gcc main.c -ldl
$ ./a.out
$ 3
$ 3
$ 3
$ 3
....(1秒間隔で出力される)
共有ライブラリのaddが呼び出されてadd(1,2)の結果が正しく(1+2=3)出力されていることは分かります。
さてここからが本番です。
add.cの中身を
int add(int a, int b) { return a * b; }
のように書き換えてコンパイルしてみましょう。
この時、先ほど実行した a.out は停止せず動かしっぱなしにします。
別ターミナルで、
$ gcc -shared add.c -o libadd.so
すると、実行している a.outの出力はどうなるでしょうか。
3
3
3
3
2
2
2
2
2に変わっています!
ということで実行中に関数の戻り値を変える事ができました。
わざわざ共有ライブラリを動的にロードするのはまどろっこしい気もしますが規模が大きいテストではこちらの手法の方が保守性が高い気もします。
JITコンパイルについて
rubyでも 2.6 からJITコンパイルが取り入れられたそうですが、今回試した実行時にライブラリを差し替えるのはアプローチとしては同じようなイメージになるかと思います。今回のadd関数は1行で終わるシンプルな関数なので面白くありませんが、これが処理時間のかかる関数だったとして実行中に処理時間のかからない関数に差し替える事ができれば嬉しいですよね。実行中のデータなどで関数の処理にフィードバックをかけて最適な処理にしていくといったこともできます。
*2
感想
同じような事は以前にも試してみていたのですが、単体テストなんかで使えるテクニックだとは気づいていませんでした。
この手のテクニックはどの言語でも何かしら手段が提供されていると思います。
私の知っている範囲ではC言語ですと、BINARY HACKS、RubyですとメタプログラミングRubyでそういったテクニックが解説されています。
Binary Hacks ―ハッカー秘伝のテクニック100選
- 作者: 高林哲,鵜飼文敏,佐藤祐介,浜地慎一郎,首藤一幸
- 出版社/メーカー: オライリー・ジャパン
- 発売日: 2006/11/14
- メディア: 単行本(ソフトカバー)
- 購入: 23人 クリック: 383回
- この商品を含むブログ (223件) を見る
- 作者: Paolo Perrotta,角征典
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/10/10
- メディア: 大型本
- この商品を含むブログ (3件) を見る
メタプログラミング.Netという本もあるんですね。面白そうなので読んでみたいと思います。
- 作者: KevinHazzard,JasonBock
- 出版社/メーカー: 角川アスキー総合研究所
- 発売日: 2013/12/11
- メディア: Kindle版
- この商品を含むブログを見る
動的に何かを変更するのってなんかかっこよくて面白いですね。