読者です 読者をやめる 読者になる 読者になる

gdcc - D を C から触るブリッジというかなんというか

Program D Bin

C から D を呼びたい場合、が仮にあるとすれば、まあ常識的には C 向けインターフェイスを書くわけです。ただその際インターフェイスのコードによるほんの少しの速度低下とコード増大と、それなりに発生する手間というオーバヘッドがあります。手間を削減する方法としては SWIG なんてのがあってこれはすばらしいものです。ですがネイティブ言語どうしなんだからマングリングと呼出し規則さえなんとかすれば直接呼べるはずなのになあ…というもどかしさが少しあったりします。

説明飽きたので実物を。

d_test.d:

class C {
    this(int i0) { i = i0; }
    int p() {
        return i;
    }
    int i;
}

void d_test() {
    C c = new C(3);
    int i = c.p();
    printf("%d in D\n", i);
    delete c;
    return;
}

extern(C) void c_test();

int main() {
    c_test();
    d_test();
    return 0;
}

まあ普通のコード。ここで定義されてない c_test はどんなものかというと…

c_test.c:

void* _d_newclass(void*);
void _d_delclass(void**);

void* d_test_C__ctor(void*, int);
int d_test_C_p(void*);

extern struct {} d_test_C;

void c_test() {
    void* cc;
    void* c;
    int i;

    cc = _d_newclass(&d_test_C);
    c = d_test_C__ctor(cc, 3);
    i = d_test_C_p(c);
    printf("%d in C\n", i);
    _d_delclass(&c);
}

c_test は d_test とだいたい等価なことをしています。 d_test_C はクラス C の TypeInfo で、 d_test_C__ctor はコンストラクタ、 d_test_C_p はメソッドメソッドを呼ぶ時は第一引数がオブジェクトにする、 new する時は _d_newclass を先に呼んでおく、 delete は _d_delclass を使う、です。

もちろんこんなもん普通にコンパイルしてもリンク通りません。それを解決するのが謎の物体 gdcc でして、こいつでコンパイルした D ファイルは C から呼び出しやすいように demangling されます。

今回は技術的に可能かどうかを知りたかっただけなので実現はとてもいい加減で、かつ未実装だらけです。

実現法。まず普通に gdc -c でコンパイル。できたオブジェクトを nm に喰わせてそのファイルで定義されたシンボルを連想配列に保存。次に gdc -S でアセンブラを吐く。で、その中のシンボルでさきの nm の時に保存されてるものは dfilt.d を少し書きかえたもの(gdcc.d に含まれています) で demangle して別なアセンブラファイルとして出力。で、 gcc -c でそれをアセンブル

問題点。オーバーロード、ポリモーフィリズム、テンプレート、 D のオブジェクトがリンクできなくなる(phobos も含めて全てを gdcc でコンパイルしちまって nm のチェックを外せば良い、はず)。そもそも外部プロセスに依存しすぎ。などなど、実用は全く想定していません。

要するにアセンブラの段階でシンボルを書き変える、っていうのがなんとなく魅力的に感じられただけです。常識的に考えるとコンパイラをいじるんでしょうね。もしくはリンカ。このへんは必要な知識が多いのでたいへんだから簡単にやってみようと。後は全てダイナミックリンクするようにして dlopen を上書きというのも面白げ。

雑記に突入

Misc

少し抵抗があるのですがこういうのもメタプログラムなんだろうか…

Level of Meta (http://www.kmonos.net/wlog/40.php#_2326040708)

なんで抵抗あるかというと… GCC が RTL 生成してそこからバイナリってのも抵抗あるな…ていうか MixJuice も抵抗が。ええと何故だ…人が読めるコードを人が読めなくて (いや読める人はいるのだろうけど) 機械の実行効率が良いコードに変換する過程の作業は全てコンパイル/アセンブルだという固定観念があるのだろうか。

そういやパース時のメタプログラミングについてwoさんとこで少し話したのですけどその関係で camlp4 をながめる時間が欲しい。

あともひとつ、 Io で数学的な 0 < x < 3 を実現。 Io じゃなくても Nil が 0 と違ってオペレータオーバーロードがある言語だとなんでもできる。別にパーサいじってるわけじゃない。

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