英語版
このページの英語版を見る

C言語とのインターフェイス

Dは、ターゲットシステムのCコンパイラに無理なく適合するように設計されている。 ラッパー関数を必要とせずに、C関数を直接呼び出すことができる。

これは、Cコンパイラのデータ型、レイアウト、 関数呼び出し/戻り値シーケンスを一致させることによって行われる。

ImportC コンパイラ拡張機能により.c ファイルを直接インポートまたはコンパイルすることができる。

一般的なCライブラリ用のバインディングは、 標準ライブラリおよび パッケージリポジトリで見つけることができる。

このページの残りの部分では、C言語とのインターフェイスのマニュアル、低レベルな側面について説明する。

C関数の呼び出し

C関数はDから直接呼び出すことができる。ラッパー関数や引数の入れ替えは必要なく、 C関数を 別のDLLに入れる必要もない。

C関数は宣言され、呼び出し規約が指定されなければならない。 おそらくは「C」呼び出し規約が指定されるだろう。例えば:

extern (C) int strcmp(const char* string1, const char* string2);
その後、Dコード内で明白な方法で呼び出すことができる。
import std.string;
int myDfunction(char[] s)
{
    return strcmp(std.string.toStringz(s), "foo");
}

ここで起こっていることはいくつかある。

Cコードは、D関数を呼び出すことができる。ただし、D関数が Cコンパイラと互換性のある属性を使用している場合に限る。最も可能性が高いのは、 extern (C)である。

// myfunc()は任意のC関数から呼び出すことができる
extern (C)
{
    void myfunc(int a, int b)
    {
        ...
    }
}

ストレージの割り当て

Cコードは、malloc()およびfree()の呼び出しによりメモリを明示的に管理する。 Dは、Dのガベージコレクタを使用してメモリを割り当てるため、 明示的な解放は必要ない。

D core.stdc.stdlib.malloc() および core.stdc.stdlib.free() を使用して明示的にメモリを割り当てることも可能である。 これらは、malloc'd バッファを必要とする C 関数に接続する際に役立つ。

D ガベージコレクタが割り当てたメモリへのポインタが C 関数に渡される場合、 そのメモリが C 関数がそのメモリを使用し終える前にガベージコレクタによって収集されないようにすることが重要である。 これは、以下の方法で実現できる。

割り当てられたメモリブロックへの内部ポインタがあれば、 GCに オブジェクトが使用中であることを知らせるのに十分である。つまり、 割り当てられたメモリの先頭へのポインタを維持する必要はない。

ガーベッジコレクタは、Dスレッドインターフェースによって作成されていないスレッドのスタックをスキャンしない。 また、他のDLLのデータセグメントなどもスキャンしない

データ型互換性

DおよびC型の同等性
DC
32ビット64ビット
void void
byte signed char
ubyte unsigned char
char char (Dでは文字は符号なし)
wchar wchar_t ( が2の場合)sizeof(wchar_t)
dchar wchar_t ( が 4 の場合)sizeof(wchar_t)
short short
ushort unsigned short
int int
uint unsigned
core.stdc.config.c_long long long
core.stdc.config.c_ulong unsigned long unsigned long
core.stdc.stdint.intptr_t intptr_t intptr_t
core.stdc.stdint.uintptr_t uintptr_t uintptr_t
long long long long (または )long long
ulong unsigned long long unsigned long (または )unsigned long long
float float
double double
real long double
cdouble double _Complex
creal long double _Complex
struct struct
union union
enum enum
class 該当なし
type * type *
type[dim] type[dim]
type[dim], type()[dim] type[dim], type()[dim]
type[] 同等品なし
type1[type2] 同等品なし
type function(params) type(*)(params)
type delegate(params) 同等品なし
size_t size_t
ptrdiff_t ptrdiff_t

これらの等価性は、ほとんどのCコンパイラに当てはまる。C標準規格では、 "型"のサイズを特定していないため、注意が必要である。

C関数へのD配列引数の渡し方

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

D および C 関数プロトタイプの同等性
D typeC type
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コードから直接呼び出すことができる。

xml-ph-0000@deepl.internal
import core.stdc.stdio;

int main()
{
    printf("hello world\n");
    return 0;
}

C言語と同様に、"値"の印刷も可能である。

int apples;
printf("there are %d apples\n", apples);

D 型にフォーマット指定子を正確に一致させることが必要である。 D コンパイラは printf フォーマットを認識し、 引数との不一致を診断する。D で使用されるフォーマットの仕様は C99 仕様 7.19.6.1 である。

引数と書式指定子の一致に関する寛大な解釈が 適用される。例えば、符号なし型を 符号付き書式指定子で表示できる。診断された非互換性は 次のとおりである。

文字列

文字列は直接印刷できない。しかし、%.*s は使用できる。

string s = "betty";
printf("hello %.*s\n", cast(int) s.length, s.ptr);

int へのキャストが必要である。

size_t およびptrdiff_t

これらはそれぞれ、zutd のフォーマット指定子を使用している。

import core.stdc.stdio : printf;
int* p = new int, q = new int;
printf("size of an int is %zu, pointer difference is %td\n", int.sizeof, p - q);

非標準フォーマット指定子

標準外の書式指定子はコンパイラによって拒否される。 文字列リテラルとしての書式のみがチェックされるため、 標準外の書式を使用できる。

xml-ph-0000@deepl.internal
const char* format = "value: %K\n";
printf(format, value);

現代的な書式の記述

書式付き出力用の改良されたD関数は、std.stdio.writef()

構造体と共用体

D 構造体および共用体は、C言語の構造体および共用体に類似している。

Cコードでは、構造体のメンバーのアラインメントやパッキングを、 コマンドラインスイッチや実装固有のさまざまな#pragmaで調整することが多い。 Dでは、Cコンパイラのルールに対応する明示的なアラインメント属性をサポートしている。 Cコードがどのアラインメントを使用しているかを確認し、 Dの構造体宣言で明示的に設定する。

Dはビットフィールドをサポートしていない。必要であれば、 シフトおよびマスク操作でエミュレート するか、std.bitmanip.bitfieldsライブラリ型を使用する。 htodはビットフィールドをインライン関数に変換し、

Dでは、匿名構造体型の変数を宣言することはサポートされていない。そのような場合は、Dで名前付き構造体を定義し、それをプライベートにする。

union Info  // Cのコード
{
    struct
    {
        char *name;
    } file;
};
union Info  // Dのコード
{
    private struct File
    {
        char* name;
    }
    File file;
}

コールバック

DはCのコールバック(関数ポインタ)を簡単に呼び出すことができ、Cは コールバックがextern(C) 関数である場合、または 両者が合意したその他のリンク(例えばextern(Windows) )である場合、Dコードが提供するコールバックを呼び出すことができる。

CコードからDコードへのコールバックの例を以下に示す。

void someFunc(void *arg) { printf("Called someFunc!\n"); }  // Cのコード
typedef void (*Callback)(void *);
extern "C" Callback getCallback(void)
{
    return someFunc;
}
extern(C) alias Callback = int function(int, int);  // Dのコード
extern(C) Callback getCallback();
void main()
{
    Callback cb = getCallback();
    cb();  // コールバックを呼び出す
}

Cコードへのコールバックを提供するDコードの例:

extern "C" void printer(int (*callback)(int, int))  // Cのコード
{
    printf("calling callback with 2 and 4 returns: %d\n", callback(2, 4));
}
extern(C) alias Callback = int function(int, int);  // Dのコード
extern(C) void printer(Callback callback);
extern(C) int sum(int x, int y) { return x + y; }
void main()
{
    printer(&sum);
}

コールバックの詳細については、クロージャのセクションを参照のこと。

既存のCライブラリの使用

DはCコードを直接呼び出すことができるため、Cライブラリの関数も呼び出すことができ、 Dは既存のCライブラリの豊富な機能を利用することができる。 ただし、そのためにはDインターフェイス(.di)ファイルを記述する必要がある。これは、 CライブラリのC.hヘッダーファイルをDに変換したものである。

一般的なCライブラリの場合、対応するDインターフェイスファイルを探す最初の場所は Deimosプロジェクトである。 まだ存在しない場合は、Deimosプロジェクトに寄稿するよう お願いする。

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

C グローバル変数は D から直接アクセスできる。C グローバル変数は C の命名規則に従うため、extern (C) ブロック内に置かなければならない。extern ストレージクラスを使用すると、グローバル変数が D コードではなく C コードで割り当てられていることを示すことができる。 C グローバル変数のデフォルトはスレッドローカルではなくグローバルなストレージである。 D からグローバルストレージを参照するには__gshared ストレージクラスを使用する。

extern (C) extern __gshared int x;