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"); }
ここで起こっていることはいくつかある。
- DはCの関数名がどのように「マングル」されるか、また 正しいC関数の呼び出し/戻り値のシーケンスを理解している。
- C関数は、同じ名前を持つ別のC関数でオーバーロードすることはできない。
- __cdecl 、__far 、__stdcall 、 __declspec、 またはその他のC 拡張型修飾子は Dには存在しない。これらはextern (C) のようなリンケージ属性で 扱われる。
- D言語には揮発性型修飾子はない。 揮発性変数を使用するC関数を宣言するには、宣言からキーワードを削除するだけでよい。
- Dでは文字列は0で終端されない。この点についての詳細は「データ型の互換性」を参照のこと。 ただし、Dの文字列リテラルは 0で終端される。
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 関数がそのメモリを使用し終える前にガベージコレクタによって収集されないようにすることが重要である。 これは、以下の方法で実現できる。
- core.stdc.stdlib.malloc() を使用してデータのコピーを作成し、 そのコピーを代わりに渡す。
- スタック上にポインタを残す(パラメータまたは 自動変数として)。ガベージコレクタはスタックをスキャンするので、
- 静的データセグメントにポインタを残す。 ガベージコレクタは静的データセグメントをスキャンする。
- ガベージコレクタにポインタを登録する std.gc.addRoot() または std.gc.addRange() コール。
割り当てられたメモリブロックへの内部ポインタがあれば、 GCに オブジェクトが使用中であることを知らせるのに十分である。つまり、 割り当てられたメモリの先頭へのポインタを維持する必要はない。
ガーベッジコレクタは、Dスレッドインターフェースによって作成されていないスレッドのスタックをスキャンしない。 また、他のDLLのデータセグメントなどもスキャンしない
データ型互換性
D | C | |
---|---|---|
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 type | C 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.internalimport 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 指定子への非ポインタ引数
- 標準外のフォーマット
- C99による未定義の動作
文字列
文字列は直接印刷できない。しかし、%.*s は使用できる。
string s = "betty"; printf("hello %.*s\n", cast(int) s.length, s.ptr);
int へのキャストが必要である。
size_t およびptrdiff_t
これらはそれぞれ、zu とtd のフォーマット指定子を使用している。
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.internalconst 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;
DEEPL APIにより翻訳、ところどころ修正。
このページの最新版(英語)
このページの原文(英語)
翻訳時のdmdのバージョン: 2.109.1
ドキュメントのdmdのバージョン: 2.109.1
翻訳日付 :
HTML生成日時:
編集者: dokutoku