条件コンパイル
Conditional compilation とは、 コンパイルするコードとしないコードを選択する過程のことを言います。 (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 が満たされた時に、その後ろの CCDeclarationBlock か Statement がコンパイルされます。 条件が満たされなかったときは、else がもしあればその後ろの CCDeclarationBlock か Statement がコンパイルされます。
例えコンパイルされない CCDeclarationBlock や Statement であっても、 文法的には正しいD言語のコードである必要があります。
新しいスコープは導入されません。 例え CCDeclarationBlock や Statement が { } で囲まれていたとしてもです。
ConditionalDeclaration と ConditionalStatement はネストすることが可能です。
StaticAssert は、 コンパイル時にエラーを表示する機能です。 条件コンパイルの分岐でエラーとしたい部分などに使います。
Condition は以下の形式で記述します:
Condition:
VersionCondition
DebugCondition
StaticIfCondition
version 条件
VersionCondition:
version ( IntegerLiteral )
version ( Identifier )
version ( unittest )
version は、 一つのソースファイルで複数バージョンのモジュールを実装することを可能にします。
VersionCondition 条件は、IntegerLiteral が現在の バージョンレベル 以上の時か、 Identifier が現在の バージョン識別子 にマッチしたときに満たされます。
バージョンレベル と バージョン識別子 は、 コマンドラインの -version スイッチで指定するか、 モジュールの中の VersionSpecification で指定するか、 コンパイラの定義済みバージョンであるかのいずれかです。
デバッグ識別子は専用の名前空間を持つので、 バージョン識別子をはじめとする他の識別子とは衝突しません。 デバッグ識別子は書かれたモジュール内にのみ影響し、 他にimportされたモジュールには影響しません。
int k;
version (Demo) // デモ版ではこのブロックをコンパイルする
{ int i;
int k; // エラー。kが既に定義されている
i = 3;
}
x = i; // 上で定義された i を使用
version (X86)
{
... // カスタムのインラインアセンブラ版を実装
}
else
{
... // デフォルトの、遅い版
}
version(unittest) は、 コードが単体テスト付きでコンパイルされている時 (dmd の -unittest のオプション) のみ有効になります。
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 | コンパイラは DMD (Digital Mars D) である |
GNU | コンパイラは GDC (GNU D Compiler) である |
LDC | コンパイラは LDC (LLVM D Compiler) である |
SDC | コンパイラは SDC (Stupid D Compiler) である |
Windows | Microsoft Windows システム |
Win32 | Microsoft 32-bit Windows システム |
Win64 | Microsoft 64-bit Windows システム |
linux | Linux システム |
OSX | Mac OS X |
FreeBSD | FreeBSD |
OpenBSD | OpenBSD |
BSD | そのほかの BSDs |
Solaris | Solaris |
Posix | 全ての POSIX システム (Linux, FreeBSD, OS X, Solaris, などを含む) |
AIX | AIX |
Haiku | Haiku OS |
SkyOS | SkyOS |
SysV3 | System V Release 3 |
SysV4 | System V Release 4 |
Hurd | GNU Hurd |
Android | Android プラットフォーム |
Cygwin | Cygwin 環境 |
MinGW | MinGW 環境 |
X86 | Intel と AMD の 32-bit プロセッサ |
X86_64 | AMD と Intel の 64-bit プロセッサ |
ARM | ARM (32-bit) |
Thumb | ARM (32-bit) Thumb モード |
PPC | PowerPC, 32-bit |
PPC64 | PowerPC, 64-bit |
IA64 | Itanium (64-bit) |
MIPS | MIPS, 32-bit |
MIPS64 | MIPS, 64-bit |
SPARC | SPARC, 32-bit |
SPARC64 | SPARC, 64-bit |
S390 | System/390, 32-bit |
S390X | System/390X, 64-bit |
HPPA | HP PA-RISC, 32-bit |
HPPA64 | HP PA-RISC, 64-bit |
SH | SuperH, 32-bit |
SH64 | SuperH, 64-bit |
Alpha | Alpha |
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 により) 生成される |
D_SIMD | ベクトル演算拡張 (__vector と __simd) 対応 |
D_Version2 | D バージョン 2 のコンパイラである |
unittest | 単体テスト が生成される (コマンドラインスイッチ -unittest) |
none | 決して定義されない。コードの一部を無効にするのに使用される |
all | 常に定義される。none の反対として使用される |
以下の識別子は定義されますが、廃止される予定で非推奨です。
バージョン識別子 | 説明 |
---|---|
darwin | Darwin OS。代わりに OSX を使います |
新しい実装が出現した場合、他にも意味のある識別子があれば追加されるでしょう。
D言語が、年を重ねるに従って発展していくのは不可避です。 それゆえ、"D_" で始まるバージョン識別子は、 D の新しい機能への対応や、 規格への対応を示す識別子として予約されています。
ここにあげた定義済みバージョン識別子は、 コマンドラインやversion設定文で設定することは禁止されています。 (これによって、例えば Windows と linux が同時に設定されているような事態を防止します。)
コンパイラベンダ特有のバージョンは、 そのベンダを示す識別子を接頭辞として、あらかじめ定義されます。 例えば:
version(DigitalMars_funky_extension)
{
...
}
適切なバージョン識別子を適切な目的に使うことを心がけてください。 例えば、ベンダIDはベンダ特有の機能を使うときに利用し、 OSのIDはOS特有の機能を使うときにのみ利用する、 などなど。
debug 条件
DebugCondition:
debug
debug ( IntegerLiteral )
debug ( Identifier )
日常的にビルドされる2つのバージョンがあります。 それは、リリースビルドとデバッグビルド。 デバッグビルドは普通、 余分なエラーチェックコードや、 もろもろのテスト、pretty-printing用のコードなどを含んでいます。 debug文の内容は、条件コンパイルされます。 これは、 Cでは #ifdef DEBUG / #endif で書いていたものの D 版です。
コンパイラに -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型に変換できない型だったり、 コンパイル時に値が決定できなかったりするとエラーになります。
StaticIfCondition は モジュール、クラス、テンプレート、構造体、共用体、関数のスコープで使用できます。 関数スコープで使われた場合は、 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 は short
INT!(17) c; // エラー。static assert で止まる
StaticIfConditional の IfStatement との違いは次の通りです:
- 文だけでなく、 宣言を条件コンパイルすることも可能です。
- たとえ { } が使われていても、 新しいスコープは導入しません。
- 非成立な条件式の中身のコードは、 文法的にさえ正しければ、 意味論的に正しい必要はありません。
- コンパイル時に必ず評価可能です。
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 は主に、 そのコードでサポートされていないコンパイル条件を示すために役に立ちます。
オプションとして、AssignExpression には第二引数を指定できます。第二引数にはエラーに関する追加情報を文字列で入れるなどしておくと、 エラーメッセージとして表示されるようになります。