D 1.0   D 2.0
About Japanese Translation

Last update Wed Nov 10 12:48:19 2010

条件コンパイル

条件コンパイルとは、 コンパイルするコードとしないコードを選択する過程のことを言います。 (C や C++ では、条件コンパイラは、プリプロセッサの #if / #else / #endif によって実現されていました。)

ConditionalDeclaration:
    Condition CCDeclarationBlock
    Condition CCDeclarationBlock else CCDeclarationBlock
    Condition : Declarations

CCDeclarationBlock:
>     Declaration
    { Declarations }
    { }

Declarations:
    Declaration
    Declaration Declarations

ConditionalStatement:
    Condition NoScopeNonEmptyStatement
    Condition NoScopeNonEmptyStatement else NoScopeNonEmptyStatement

条件 Condition が満たされた時に、その後ろの CCDeclarationBlockStatement がコンパイルされます。 条件が満たされなかったときは、else がもしあればその後ろの CCDeclarationBlock or Statement がコンパイルされます。

例えコンパイルされない CCDeclarationBlockStatement であっても、 文法的には正しいD言語のコードである必要があります。

新しいスコープは導入されません。 例え CCDeclarationBlockStatement{ } で囲まれていたとしてもです。

ConditionalDeclarationConditionalStatement はネストすることが可能です。

StaticAssert は、 コンパイル時にエラーを表示する機能です。 条件コンパイルの分岐でエラーとしたい部分などに使います。

Condition は以下の形式で記述します:

Condition:
    VersionCondition
    DebugCondition
    StaticIfCondition

Version 条件

Version は、 一つのソースファイルで複数バージョンのモジュールを実装することを可能にします。

VersionCondition:
	version ( IntegerLiteral )
	version ( Identifier )

VersionCondition 条件は、IntegerLiteral が現在の バージョンレベル 以上の時か、 Identifier が現在の バージョン識別子 にマッチしたときに満たされます。

バージョンレベルバージョン識別子 は、 コマンドラインの -version スイッチで指定するか、 モジュールの中の VersionSpecification で指定するか、 コンパイラの定義済みバージョンであるかのいずれかです。

デバッグ識別子は専用の名前空間を持つので、 バージョン識別子をはじめとする他の識別子とは衝突しません。 デバッグ識別子は書かれたモジュール内にのみ影響し、 他にimportされたモジュールには影響しません。

int k;
version (Demo)	// デモ版ではこのブロックをコンパイルする
{   int i;
    int k;	// エラー。kが既に定義されている

    i = 3;
}
x = i;		// 上で定義された i を使用
version (X86)
{
    ... // カスタムのインラインアセンブラ版を実装
}
else
{
    ... // デフォルトの、遅い版
}

Version 条件指定

VersionSpecification:
    version = Identifier ;
    version = IntegerLiteral ;

バージョン条件指定を使うと、 メジャーバージョン毎に導入したい機能の切り分けなどが簡単になります。例えば:

version (ProfessionalEdition)
{
    version = FeatureA;
    version = FeatureB;
    version = FeatureC;
}
version (HomeEdition)
{
    version = FeatureA;
}
...
version (FeatureB)
{
    ... Feature B の実装 ...
}

バージョン識別子やバージョン番号の前方参照は、不正です:

version (Foo)
{
    int x;
}
version = Foo;	// エラー、Foo は既に使用されている

VersionSpecification はモジュールスコープでのみ記述できます。

version条件は debug条件に非常によく似ていますが、 目的は全く異なります。 debugはリリース版では取り除きたいデバッグ用の コードのためであるのに対して、version文は、 同じソースから複数のリリース版を作るための機能です。

以下は、full バージョンと demo バージョンを区別する例です:

class Foo
{
    int a, b;

    version(full)
    {
	int extrafunctionality()
	{
	    ...
	    return 1;		// 発展的な機能のサポート
	}
    }
    else // demo
    {
	int extrafunctionality()
	{
	    return 0;		// 発展的な機能はサポートされない
	}
    }
}
versionへのパラメタによって、 複数種類のバージョンをビルドできます:
version(n) // バージョンレベル >= n ならコードを追加
{
   ... version code ...
}

version(identifier) // バージョン識別子が
                         // identifier ならコードを追加
{
   ... version code ...
}

これらは、あらかじめコマンドラインから -version=n-version=identifier でも指定できます。

定義済みのバージョン識別子

環境に関するいくつかのバージョン識別子は、 一貫した使われ方をされるように、予め定義されています。 バージョン識別子はその他の種類の識別子とは衝突しない、 別の名前空間におかれています。 定義済みバージョン識別子は、グローバルです。 つまり、コンパイルやimportされる全てのモジュールに影響します。

定義済みバージョン識別子
バージョン識別子 説明
DigitalMars Digital Mars がコンパイラベンダである
X86 Intel と AMD の 32bit プロセッサ
X86_64 AMD と Intel の 64 bit プロセッサ
Windows Microsoft Windows システム
Win32 Microsoft 32bit Windows システム
Win64 Microsoft 64 bit Windows システム
linux 全ての Linux システム
D_NET .NET
OSX Mac OS X
FreeBSD FreeBSD
Solaris Solaris
LittleEndian バイト順序が、LSB-First
BigEndian バイト順序が、MSB-First
D_Coverage カバレッジ解析命令が(-cov スイッチにより)生成される
D_Ddoc Ddoc ドキュメント化 (コマンドラインスイッチ -D) の生成中
D_InlineAsm_X86 X86インラインアセンブラが実装されている
D_InlineAsm_X86_64 X86-64インラインアセンブラが実装されている
D_LP64 ポインタが64ビット (コマンドラインスイッチ -m64)
D_PIC 位置独立コードが (-fPIC スイッチにより) 生成される
none 決して定義されない。コードの一部を無効にするのに使用される
all 常に定義される。none の反対として使用される

新しい実装が出現した場合、他にも意味のある識別子があれば追加されるでしょう。

D言語が、年を重ねるに従って発展していくのは不可避です。 それゆえ、"D_" で始まるバージョン識別子は、 D の新しい機能への対応や、 規格への対応を示す識別子として予約されています。

ここにあげた定義済みバージョン識別子は、 コマンドラインやversion設定文で設定することは禁止されています。 (これによって、例えば Windowslinux が同時に設定されているような事態を防止します。)

コンパイラベンダ特有のバージョンは、 そのベンダを示す識別子を接頭辞として、あらかじめ定義されます。 例えば:

version(DigitalMars_funky_extension)
{
    ...
}

適切なバージョン識別子を適切な目的に使うことを心がけてください。 例えば、ベンダIDはベンダ特有の機能を使うときに利用し、 OSのIDはOS特有の機能を使うときにのみ利用する、 などなど。

Debug 条件

日常的にビルドされる2つのバージョンがあります。 それは、リリースビルドとデバッグビルド。 デバッグビルドは普通、 余分なエラーチェックコードや、 もろもろのテスト、pretty-printing用のコードなどを含んでいます。 debug文の内容は、条件コンパイルされます。 これは、 C では #ifdef DEBUG / #endif で書いていたものの D 版です。

DebugCondition:
    debug
    debug ( IntegerLiteral )
    debug ( Identifier )

コンパイラに -debug スイッチが投げられた時に、 debug 条件が満たされます。

debug ( IntegerLiteral ) 条件は、デバッグレベル >= IntegerLiteral ならば条件が満たされたことになります。

debug ( Identifier ) 条件は、デバッグ識別子が Identifier と一致すれば満たされます。

class Foo
{
	int a, b;
    debug:
	int flag;
}

Debug 条件指定

DebugSpecification:
    debug = Identifier ;
    debug = IntegerLiteral ;

デバッグ識別子とデバッグレベルは、 -debug コマンドライン指定か、DebugSpecification で指定します。

デバッグ条件指定は、書かれたモジュール内にのみ影響し、 importされたモジュールには影響しません。デバッグ識別子は専用の名前空間を持つので、 バージョン識別子をはじめとする 他の識別子とは衝突しません。

デバッグ条件指定の前方参照は、不正です:

debug (foo) writefln("Foo");
debug = foo;	// エラー、fooは設定される前に使用されている

DebugSpecification はモジュールスコープでのみ記述できます。

debugへのパラメタによって、 複数種類のデバッグビルドを生成できます:

debug(IntegerLiteral) { }    // デバッグレベル >= IntegerLiteral ならコードを追加
debug(identifier) { } // デバッグ識別子が identifier ならコードを追加

これらは、あらかじめコマンドラインから -debug=n-debug=identifier でも指定できます。

static if 条件

StaticIfCondition:
    static if ( AssignExpression )

AssignExpression はコンパイル時に評価され、 暗黙にbool型に変換されます。 評価結果が true なら条件成立で、 false ならば非成立です。

AssignExpression がbool型に変換できない型だったり、 コンパイル時に値が決定できなかったりするとエラーになります。

StaticIf条件 は モジュール、クラス、テンプレート、構造体、共用体、関数のスコープで使用できます。 関数スコープで使われた場合は、 AssignExpression で参照できるシンボルはその位置で普通に参照できるもの全てです。

const int i = 3;
int j = 4;

static if (i == 3)	// ok、モジュールスコープ
    int x;

class C
{   const int k = 5;

    static if (i == 3)	// ok
	int x;
    else
	long x;

    static if (j == 3)	// エラー、j は定数ではない
	int y;

    static if (k == 5)	// ok、k はカレントスコープの定数
	int z;
}

template INT(int i)
{
    static if (i == 32)
	alias int INT;
    else static if (i == 16)
	alias short INT;
    else
	static assert(0);	// 未サポート
}

INT!(32) a;	// a は int
INT!(16) b;	// b is a short
INT!(17) c;	// エラー。static assert で止まる

StaticIfConditionalIfStatement との違いは次の通りです:

  1. 文だけでなく、 宣言を条件コンパイルすることも可能です。
  2. 例え { } が使われていても、 新しいスコープは導入しません。
  3. 非成立な条件式の中身のコードは、 文法的にさえ正しければ、 意味論的に正しい必要はありません。
  4. コンパイル時に必ず評価可能です。

Static Assert

StaticAssert:
    static assert ( AssignExpression );
    static assert ( AssignExpression , AssignExpression );

AssignExpression はコンパイル時に評価され、ブール値に変換されます。 その値がtrueであれば、static assert は無視されます。 false な場合、 診断メッセージが出力され、コンパイルが失敗します。

AssertExpression とは違い、StaticAssert は、 不成立の条件の中にあるのでなければ常にコンパイラによって評価、 テストされます。

void foo()
{
    if (0)
    {
	assert(0);	  // 決して引っ掛からない
	static assert(0); // 常に引っ掛かる
    }
    version (BAR)
    {
    }
    else
    {
	static assert(0); // BARが定義されていない限り引っ掛からない
    }
}

StaticAssert は主に、 そのコードでサポートされていないコンパイル条件を示すために役に立ちます。

オプションとして、AssertExpression には第二引数を指定できます。第二引数にはエラーに関する追加情報を文字列で入れるなどしておくと、 エラーメッセージとして表示されるようになります。