Improve this page Github へのログインが必要です。 簡単な修正は、ここから fork、オンライン編集、pull request ができます。 大きな修正については、 通常の clone で行って下さい。 Page wiki 関連するWikiページを参照・編集 English このページの英語版(原文)

条件コンパイル

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 が満たされた時に、その後ろの CCDeclarationBlockStatement がコンパイルされます。 条件が満たされなかったときは、else がもしあればその後ろの CCDeclarationBlockStatement がコンパイルされます。

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

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

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

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設定文で設定することは禁止されています。 (これによって、例えば Windowslinux が同時に設定されているような事態を防止します。)

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

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 で止まる

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 は主に、 そのコードでサポートされていないコンパイル条件を示すために役に立ちます。

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