Improve this page Github へのログインが必要です。 簡単な修正は、ここから fork、オンライン編集、pull request ができます。 大きな修正については、 通常の clone で行って下さい。 Page wiki 関連するWikiページを参照・編集 English このページの英語版(原文)

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 の対応する型
DC
32 bit 64 bit
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
c_long (core.stdc.config で定義) long long
c_ulong (core.stdc.config で定義) unsigned long unsigned long
long long long long (または long long)
ulong unsigned long long unsigned long (または unsigned long long)
float float
double double
real long double
struct struct
union union
enum enum
class なし
type* type *
type[dim] type[dim]
type[dim]* type(*)[dim]
type[] なし
type[type] なし
type function(parameters) type(*)(parameters)
type delegate(parameters) なし
size_t size_t
ptrdiff_t ptrdiff_t

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

Dの配列をCの関数に渡す

Cでは、例え関数プロトタイプでは配列であるかのように宣言されていたとしても、 配列はポインタとして渡されます。Dでは、静的配列は参照渡しではなく、値渡しされます。 従って、 関数プロトタイプはCの期待するものになっているように調整する必要があります。

D と C の同等な関数プロトタイプ
D の型 C の型
T* T[]
ref T[dim] T[dim]

例えば:

void foo(int a[3]) { ... } // C のコード
extern (C)
{
  void foo(ref int[3] a); // D でのプロトタイプ宣言
}

printf() の呼び出し

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

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

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

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

構造体と共用体

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

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

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

既存の C ライブラリを使う

D からは C のコードを直接呼び出せるので、どんな C のライブラリ関数でも、 D とリンクすれば呼び出すことができます。 これをするには、しかし、D インターフェイス (.di) ファイルを、 そのライブラリの C の .h ヘッダファイルから変換して作る必要があります。

著名な C のライブラリに関しては、まずは対応する D のインターフェイスファイルがないか、 Deimos プロジェクト をチェックしましょう。 それがまだ存在せず、あなたが書き上げたならば、ぜひ、 Deimos プロジェクトに貢献してください。.

C のグローバル変数へのアクセス

C のグローバル変数は D から直接アクセスできます。C のグローバル変数は C の命名規約に従うので、D からは extern (C) ブロックで宣言される必要があります。 extern 記憶域クラスを指定することで、そのグローバル変数が D のコードではなく C のコードの側で割り当てられていることを処理系に伝えます。 C のグローバル変数は、デフォルトではスレッドローカルではなく、グローバルな記憶域に配置されます。 グローバル記憶域を D から参照するには、 __gshared 記憶域クラスを使います。

extern (C) extern __gshared int x;