D 1.0   D 2.0
About Japanese Translation

www.digitalmars.com
Last update Sun Sep 9 16:18:02 2007

Cとのインターフェイス

D は、システムのCコンパイラと相性良く動作するように設計されています。 独自のVMを持ったりはせず、既存のCのランタイムライブラリを使うのです。 せっかく沢山のC APIが既に存在するというのに、それを D に移植したり D 用のラッパを書いたり…という作業はばかげています。 直接Cの関数を呼び出せるというのは、なんと楽なことでしょうか。

データ型やそのメモリ配置、関数の呼び出し規約をCコンパイラと合わせることで、 Cとの互換性が実現されています。

C の関数を呼び出す

Cの関数はDから直接呼び出せます。 ラッパ関数や引数のすりあわせとか、 Cの関数を別のDLLに分けたりする必要は全くありません。

Cの関数は呼び出し規約の指定付きで宣言します。大抵は "C" 呼び出し規約で問題ないでしょう。例えば:

extern (C) int strcmp(char* string1, char* string2);

すると、Dのコードからは当たり前のように呼び出すことができます:

import std.string;
int myDfunction(char[] s)
{
    return strcmp(std.string.toStringz(s), "foo");
}

何が起きているのかをもっと具体的に説明しますと:

extern (C) 属性などで Cコンパイラと互換性があるようにコンパイルされた D の関数なら、逆にCのコードからDの関数を呼び出すことも可能です:

// myfunc() はCのどの関数からも呼び出せる
extern (C)
{
    void myfunc(int a, int b)
    {
	...
    }
}

メモリ割り当て

C では、malloc() や free() によって明示的にメモリを管理します。 一方 D ではガベージコレクタでメモリを割り当てるので、 明示的な free は不要です。

mallocで確保されたバッファを要求するようなCの関数と連携するために、 D では c.stdlib.malloc() や c.stdlib.free() の呼び出しによって 明示的にメモリ管理を行うこともできます。

Dのガベージコレクタで確保したメモリへのポインタを渡すには、 Cの関数がそのメモリを使い終わる前にガベージコレクタが領域を回収してしまう、 といった事故が起きないことを確かめなくてはなりません。 これは幾つかの方法で実現できます:

オブジェクトがまだ使われていることを GC に知らせるには、 割り当てられたメモリ領域の内部へのポインタがあれば十分です。 領域の先頭へのポインタを保持しておく必要はありません。

Dによって作られた以外のスレッドのスタックや、 他のDLLに属するデータセグメントについては、 ガベージコレクタによって探索されません。

データ型の互換性

D と C の対応する型
D の型 C の型
void void
byte signed char
ubyte unsigned char
char char (Dではcharはunsignedです)
wchar wchar_t (sizeof(wchar_t) が 2 のとき)
dchar wchar_t (sizeof(wchar_t) が 4 のとき)
short short
ushort unsigned short
int int
uint unsigned
long long long
ulong unsigned long long
float float
double double
real long double
ifloat なし
idouble なし
ireal なし
cfloat なし
cdouble なし
creal なし
struct struct
union union
enum enum
class なし
type* type *
なし type &
type[dim] type[dim]
type[dim]* type(*)[dim]
type[] なし
type[type] なし
type function(parameters) type(*)(parameters)
type delegate(parameters) なし

この対応関係はほとんどの32bit C コンパイラで成り立ちます。ただし Cの標準では型のサイズは一意に定められていないので、注意が必要です。

printf() の呼び出し

これは主に、printf のフォーマット指定がDのデータ型と合うかどうか、 が問題になります。printf はNUL終端の文字列を扱うようになっていますが、 Dでのcharの動的配列はそうなっていません。 しかし配列データの前に 4バイトで配列のサイズが置かれていますので、%.*s を使うと完璧に動作します:

void foo(char[] string)
{
    printf("my string is: %.*s\n", string);
}

例の printf のフォーマット文字列リテラルは \0 で終わっていません。これは、 より大きな構造体の初期化子の一部でない限り、 文字列リテラルの後ろには \0 が格納されるようになっているためです。

書式化出力を得るためのDでのベターな関数として、 std.stdio.writef() があります。

構造体と共用体

Dの構造体と共用体は、Cのそれとほぼ同じです。

Cのコードでは、実装特有の#pragmaやコンパイラのコマンドスイッチによって、 構造体の整列を制御します。これに対応するものとして、D には 明示的なアラインメント属性が用意されています。 C側でのアラインメントを調べて、 D側の構造体宣言に明示的にその値を設定して下さい。

Dはビットフィールドをサポートしません。必要ならば、 シフトとビットマスク演算によってエミュレートできます。 htod を使うと、 ビットフィールドはシフトとマスクを使ったインライン関数に変換されます。