C の .h から D のモジュールへの変換
D は Cのソースコードを直接コンパイルすることはできませんが、 Cへのインターフェイスを書いたり、Cのオブジェクトファイルとリンクしたり、 DLL内のC関数を呼び出したりするのは簡単です。 Cのコードへのインターフェイスは通常、.h ファイルにあります。 ですからCのコードと接続する方法は、Cの.hファイルを D のモジュールへの変換です。しかしこれは、 人間の判断が必須な部分が少なからずあるため、 機械的に行うのは難しい作業です。 以下は、このような変換作業のガイドとなっています。プリプロセッサ
.h ファイルは時々、#include や #ifdef のようなマクロが何層にも重なった泥沼状態になっていることがあります。 D は C のようなテキストプリプロセッサを持たないので、 マクロを取り除くには、 まずプリプロセッサの出力を取ってくることになります。DMC (Digital Mars C/C++ compiler) では、コマンド:dmc -c program.h -e -lによって、全てのプリプロセスを終えた後のファイルが program.lst として生成されます。
#if, #ifdef, #include などの文は全て取り除いてください。
リンケージ
一般に、Cのリンケージを付与するには:extern (C) { /* ...ファイルの内容... */ }モジュール全体を上のように囲んで下さい。
型
ファイル全体に検索と置換をかけることで、 Cの型をDの型へ改名できるでしょう。 以下の表が、32bitのCのコードからの典型的な対応表になっています。
C での型 | D での型 |
---|---|
long double | real |
unsigned long long | ulong |
long long | long |
unsigned long | uint |
long | int |
unsigned | uint |
unsigned short | ushort |
signed char | byte |
unsigned char | ubyte |
wchar_t | wchar or dchar |
bool | bool, byte, int |
size_t | size_t |
ptrdiff_t | ptrdiff_t |
NULL
NULL や ((void*)0) は null に置き換えます。数値リテラル
数値リテラルの接尾辞 'L' や 'l' は取り除きます。 これは、C の long は (通常)D の int と同じサイズだからです。 同様に、接尾辞 'LL' は単一の 'L' に置き換えるべきです。 接尾辞 'u' は全て D でも同様に働きます。文字列リテラル
ほとんどの場合、接頭辞 'L' は単純に消して構いません。 D では、文字列は必要なときに暗黙にワイド文字列へ変換されます。 しかし、L"string"これをこう置き換えることもできます:
"string"w // ワイド文字が16bitの環境 "string"d // ワイド文字が32bitの環境
マクロ
次のような一連のマクロは:#define FOO 1 #define BAR 2 #define ABC 3 #define DEF 40次で代用します:
enum
{ FOO = 1,
BAR = 2,
ABC = 3,
DEF = 40
}
あるいは:
const int FOO = 1; const int BAR = 2; const int ABC = 3; const int DEF = 40;関数的マクロ、例えば:
#define MAX(a,b) ((a) < (b) ? (b) : (a))のようなものは、関数で置き換えることができます:
int MAX(int a, int b) { return (a < b) ? b : a; }関数を使う方法は、しかし、実行時ではなくコンパイル時に評価されなければならない、 静的初期化子の中では使用することができません。 コンパイル時については、テンプレートが使えます:
#define GT_DEPTH_SHIFT (0) #define GT_SIZE_SHIFT (8) #define GT_SCHEME_SHIFT (24) #define GT_DEPTH_MASK (0xffU << GT_DEPTH_SHIFT) #define GT_TEXT ((0x01) << GT_SCHEME_SHIFT) /* graphtypeを構築するマクロ */ #define GT_CONSTRUCT(depth,scheme,size) \ ((depth) | (scheme) | ((size) << GT_SIZE_SHIFT)) /* 一般的なgraphtype */ #define GT_TEXT16 GT_CONSTRUCT(4, GT_TEXT, 16)対応する D のコードは:
const uint GT_DEPTH_SHIFT = 0; const uint GT_SIZE_SHIFT = 8; const uint GT_SCHEME_SHIFT = 24; const uint GT_DEPTH_MASK = 0xffU << GT_DEPTH_SHIFT; const uint GT_TEXT = 0x01 << GT_SCHEME_SHIFT; // graphtypeを構築するテンプレート template GT_CONSTRUCT(uint depth, uint scheme, uint size) { // 定数の名前をテンプレートの名前と同じにする const uint GT_CONSTRUCT = (depth | scheme | (size << GT_SIZE_SHIFT)); } // 一般的なgraphtype const uint GT_TEXT16 = GT_CONSTRUCT!(4, GT_TEXT, 16);
宣言リスト
D では、宣言のリストの中で型を変更することを許しません。 したがって:int *p, q, t[3], *s;は次のように書かれなければなりません:
int* p, s; int q; int[3] t;
void引数リスト
引数を1個もとらない関数:int foo(void);は、Dでは:
int foo();
const 型修飾子
D の const は、型修飾子ではなく記憶クラスです。従って、 型修飾子として使われている const は全て除いてしまいます:void foo(const int *p, char *const q);は、こうなります:
void foo(int* p, char* q);
外部グローバル C 変数
Dでは常にグローバル変数の宣言は、同時に変数の定義でもあります。 しかし、リンクするCで作ったオブジェクトファイルの中にも定義があると、 多重定義エラーとなってしまいます。この問題を解決するには、 extern 記憶クラスを使うという手があります。 例えば、こんなCのヘッダーファイル foo.h は:struct Foo { }; struct Foo bar;これは次のようなDのモジュールに置き換えられます。foo.d: extern (C) { extern Foo bar; }
Typedef
D では、alias が C の typedef と同じ働きをします。typedef int foo;は、こうなります:
alias int foo;
構造体
次のような宣言は:typedef struct Foo { int a; int b; } Foo, *pFoo, *lpFoo;こう置き換えます:
struct Foo { int a; int b; } alias Foo* pFoo, lpFoo;
構造体メンバのアラインメント
D の普通の実装では、構造体のメンバは同じ環境の C コンパイラと同様に整列するようになっています。しかし、 .h ファイルの中にアラインメントを制御する #pragma が含まれている場合は、Dの align 属性で対応します:#pragma pack(1) struct Foo { int a; int b; }; #pragma pack()は、こうなります:
struct Foo { align (1): int a; int b; }
ネストした構造体
struct Foo { int a; struct Bar { int c; } bar; }; struct Abc { int a; struct { int c; } bar; };は、こうなります:
struct Foo { int a; struct Bar { int c; } Bar bar; } struct Abc { int a; struct { int c; } }
__cdecl, __pascal, __stdcall
int __cdecl x; int __cdecl foo(int a); int __pascal bar(int b); int __stdcall abc(int c);は、こうなります:
extern (C) int x; extern (C) int foo(int a); extern (Pascal) int bar(int b); extern (Windows) int abc(int c);
__declspec(dllimport)
__declspec(dllimport) int __stdcall foo(int a);は、こうなります:
export extern (Windows) int foo(int a);
__fastcall
残念なことに、D は __fastcall 呼び出し規約に対応していません。 このため、一段緩衝コードが必要になります。Cで書いた:int __fastcall foo(int a); int myfoo(int a) { return foo(int a); }を__fastcallをサポートするCコンパイラでコンパイルしてリンクするか、 それを obj2asm で逆アセンブルして D の myfoo の中に インラインアセンブラ で挿入することになります。