ccall_direct

もうちょいいじってから、と思ったけど取りあえずもういいや。

http://shinh.skr.jp/io/ccall_direct.tar.bz2

Lua やらなんやらで自分のソフトにスクリプト言語を組み込んだ経験のある人は、その組み込む言語に関数を提供するために登録する作業がめんどうだと思った人が多いと思われます。実際あの作業は面倒すぎます。

というわけで登録もへったくれもなく直接呼ぶものがこれです。ソフト側コードはこんなの。 ccall_init と ccall_quit 以外はごく普通です。

#include "ccall.h"

int test1() { return 1; }
int test2() { return 2; }

int main(int argc, char *argv[]) {
    IoState *state;

    state = IoState_new();
    IoState_pauseGarbageCollector(state);

    ccall_init("nmfile", state, state->lobby);

    IoState_resumeGarbageCollector(state);

    IoState_doFile_(state, "test_ccall.io");

    ccall_quit();

    IoState_free(state);

    return 0;

Io 側コードはこんな感じ。

write(test1, "\n")
write(test2, "\n")
err

アーカイブ内の build.sh を実行して、 ./test_ccall を実行すると、登録してないはずの test1, test2 を呼べてしまえます。最後の err は存在してないので Io の例外が飛びます。

仕組みはすごく簡単。 build.sh で nm test_ccall > nmfile などとしていて、 ccall_init で nmfile を読み込んでシンボル→関数アドレスの形でハッシュに記録。 Importer の find メソッドを横取りして、登録されてるシンボルなら C の関数を呼び、そうでなければオリジナルの find を呼ぶ。

要するに nm しておけば C でも関数名からアドレスを得ることはできるというはなし。他の応用として、例えばユニットテストとかで Java と違ってテストメソッドを登録しにゃならんのはとてもとても苦痛なのですが、そのへん解決できるのはちょっと実用的かもと思った。

問題点は引数無しの返り値 int 決め打ちなこと。そのへんは C のシンボルは型情報無いのでユーザ側で指定することになるだろうけど、 C++ とか D なら demangle すれば型情報を読めるのでもっと面白そう。 gdcc (http://d.hatena.ne.jp/shinichiro_h/20040710#p1) はそのへんの流れの一貫なのですよ…

あとダイナミックロードされるシンボルは呼べないのも問題か。

おまけ。私はどうも、

suite.addTest(&test1);
suite.addTest(&test2);
suite.addTest(&test3);
suite.addTest(&test4);
// ...

というたぐいのコードが嫌いで嫌いでしかたがないらしい。これもそれもあれも アプローチは違えど、こういうコードが大嫌いだからやっていることに思える。

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h