英語版
このページの英語版を見る

条件付きコンパイル

条件付きコンパイルとは、どのコードをコンパイルし、どのコードをコンパイルしないかを選択するプロセスである。 どのコードをコンパイルし、どのコードをコンパイルしないかを選択するプロセスである。

ConditionalDeclaration:
    Condition DeclarationBlock
    Condition DeclarationBlock else DeclarationBlock
    Condition : DeclDefsopt
    Condition DeclarationBlock else : DeclDefsopt
ConditionalStatement: Condition NoScopeNonEmptyStatement Condition NoScopeNonEmptyStatement else NoScopeNonEmptyStatement

条件が満たされていれば、次のようになる。 DeclarationBlockまたはStatementがコンパイルされる。 条件を満たさない場合は、次の宣言ブロックまたは文がコンパイルされる。 else がコンパイルされる。

コンパイルされないDeclarationBlockまたはStatementは、構文的に正しくなければならない。 は、構文的に正しくなければならない。

たとえ 新しいスコープは導入されない。 が{ }

条件付き宣言と 条件付きステートメント は入れ子にすることができる。

StaticAssertは を使うと、コンパイル時にエラーとなる条件分岐に対してエラーを出すことができる。 コンパイル時にエラーを発行することができる。

条件には以下の形式がある:

Condition:
    VersionCondition
    DebugCondition
    StaticIfCondition

バージョン条件

VersionCondition:
    version ( Identifier )
    version ( unittest )
    version ( assert )

バージョンは、あるモジュールの複数のバージョンを1つのソースファイル を実装できるようにする。

VersionConditionはIdentifierバージョン識別子と一致する場合に満たされる。

バージョン識別子は、コマンドラインで。 -version で設定するか、モジュール自体に またはコンパイラによって定義される。 またはコンパイラーによって定義される。

バージョン識別子は独自の名前空間内にあり、デバッグ識別子や他の名前空間と衝突しない。 デバッグ識別子やモジュール内の他のシンボルと衝突しない。 あるモジュールで定義されたバージョン識別子は、インポートされた他のモジュールに影響を与えない。 あるモジュールで定義されたバージョン識別子は、他のインポートされたモジュールには影響しない。

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

    i = 3;
}
x = i;      // 上記で宣言したiを使用する
version (X86)
{
    ... // カスタム・インラインアセンブラを実装する
}
else
{
    ... // デフォルトの遅いバージョンを使う
}

version(unittest) 。 コードがユニットテストを有効にしてコンパイルされている場合にのみ( の -unittestdmd のオプション)を有効にしてコンパイルした場合にのみ満たされる。

バージョン仕様

VersionSpecification:
    version = Identifier ;

バージョン指定は、次のように機能セットを1つのメジャーバージョンにまとめることを容易にする。 を1つのメジャーバージョンにまとめることができる:

version (ProfessionalEdition)
{
    version = FeatureA;
    version = FeatureB;
    version = FeatureC;
}
version (HomeEdition)
{
    version = FeatureA;
}
...
version (FeatureB)
{
    ... implement Feature B ...
}

バージョン識別子やレベルは、前方参照できない:

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

VersionSpecificationsは、モジュール・スコープでのみ使用できる。

デバッグ条件とバージョン条件は、表面的には同じ動作をする。 表面的には同じ動作をする、 その目的はまったく異なる。デバッグ文 は、リリース・バージョンでは削除されるデバッグ・コードを追加するためのものだ。 バージョン・ステートメントは、移植性と複数のリリース・バージョンを支援するためのものである。 バージョンをサポートするためのものだ。

以下は、フルバージョンの例である。 フルバージョンの例だ:

class Foo
{
    int a, b;

    version(full)
    {
        int extrafunctionality()
        {
            ...
            return 1;  // 追加関数がサポートされている
        }
    }
    else // デモ
    {
        int extrafunctionality()
        {
            return 0;  // 余分な関数はサポートされていない
        }
    }
}

様々な異なるバージョンのビルドは、パラメータ をバージョンに指定する:

version(identifier) // バージョン・キーワードが識別子の場合、
                         // バージョン・コードを追加する
{
    ... version code ...
}

これはおそらく、コマンドラインから -version=identifier.

定義済みバージョン

いくつかの環境バージョン識別子と識別子 の名前空間は、一貫した使い方のためにあらかじめ定義されている。 バージョン識別子はコード内の他の識別子と衝突しない。 別の名前空間にある。 定義済みのバージョン識別子はグローバルである。 すなわち、コンパイルおよびインポートされるすべてのモジュールに適用される。

定義済みバージョン識別子
Version IdentifierDescription
DigitalMars DMD (Digital Mars D)はコンパイラである。
GNU GDC (GNU D Compiler) はコンパイラーである。
LDC LDC (LLVM D Compiler) はコンパイラである。
SDC SDC (Stupid D Compiler) はコンパイラである。
Windows マイクロソフトのWindowsシステム
Win32 マイクロソフト32ビットWindowsシステム
Win64 マイクロソフト64ビットWindowsシステム
linux すべてのLinuxシステム
OSX macOS
iOS iOS
TVOS tvOS
WatchOS watchOS
VisionOS visionOS
FreeBSD FreeBSD
OpenBSD OpenBSD
NetBSD NetBSD
DragonFlyBSD ドラゴンフライBSD
BSD その他すべてのBSD
Solaris ソラリス
Posix すべてのPOSIXシステム(Linux、FreeBSD、OS X、Solarisなどを含む)
AIX IBMアドバンスト・インタラクティブeXecutive OS
Haiku Haikuオペレーティング・システム
SkyOS SkyOSオペレーティング・システム
SysV3 システムVリリース3
SysV4 System V リリース4
Hurd GNU Hurd
Android Androidプラットフォーム
Emscripten Emscriptenプラットフォーム
PlayStation PlayStationプラットフォーム
PlayStation4 PlayStation 4プラットフォーム
Cygwin Cygwin環境
MinGW MinGW環境
FreeStanding オペレーティングシステムのない環境(ベアメタルターゲットなど)
CRuntime_Bionic Bionic Cランタイム
CRuntime_DigitalMars DigitalMars Cランタイム
CRuntime_Glibc Glibc Cランタイム
CRuntime_Microsoft Microsoft Cランタイム
CRuntime_Musl musl Cランタイム
CRuntime_Newlib newlib Cランタイム
CRuntime_UClibc uClibc Cランタイム
CRuntime_WASI WASI Cランタイム
CppRuntime_Clang Clang Cppランタイム
CppRuntime_DigitalMars DigitalMars Cppランタイム
CppRuntime_Gcc Gcc Cppランタイム
CppRuntime_Microsoft Microsoft Cppランタイム
CppRuntime_Sun Sun Cppランタイム
X86 インテルおよびAMD 32ビットプロセッサー
X86_64 インテルおよびAMD 64ビットプロセッサー
ARM ARMアーキテクチャ(32ビット)(AArch32など)
ARM_Thumb あらゆるThumbモードのARM
ARM_SoftFloat ARMsoft 浮動小数点 ABI
ARM_SoftFP ARMsoftfp 浮動小数点 ABI
ARM_HardFloat ARMhardfp 浮動小数点 ABI
AArch64 Advanced RISC Machineアーキテクチャ(64ビット)
AsmJS 中間プログラミング言語 asm.js
AVR 8 ビット Atmel AVR マイクロコントローラー
Epiphany Epiphany アーキテクチャ
PPC PowerPCアーキテクチャ(32ビット
PPC_SoftFloat PowerPCソフトフロートABI
PPC_HardFloat PowerPCハードフロートABI
PPC64 PowerPCアーキテクチャ、64ビット
IA64 Itaniumアーキテクチャ(64ビット)
MIPS32 MIPSアーキテクチャ、32ビット
MIPS64 MIPSアーキテクチャ、64ビット
MIPS_O32 MIPS O32 ABI
MIPS_N32 MIPS N32 ABI
MIPS_O64 MIPS O64 ABI
MIPS_N64 MIPS N64 ABI
MIPS_EABI MIPS EABI
MIPS_SoftFloat MIPSsoft-float ABI
MIPS_HardFloat MIPShard-float ABI
MSP430 MSP430アーキテクチャ
NVPTX Nvidia並列スレッド実行(PTX)アーキテクチャ、32ビット
NVPTX64 Nvidia並列スレッド実行(PTX)アーキテクチャ、64ビット
RISCV32 RISC-Vアーキテクチャ、32ビット
RISCV64 RISC-Vアーキテクチャ、64ビット
SPARC SPARCアーキテクチャ、32ビット
SPARC_V8Plus SPARC v8+ ABI
SPARC_SoftFloat SPARCソフトフロートABI
SPARC_HardFloat SPARCハードフロートABI
SPARC64 SPARCアーキテクチャ、64ビット
S390 System/390アーキテクチャ、32ビット
SystemZ System Zアーキテクチャ、64ビット
HPPA HP PA-RISCアーキテクチャ、32ビット
HPPA64 HP PA-RISCアーキテクチャ、64ビット
SH SuperHアーキテクチャ、32ビット
WebAssembly WebAssembly仮想ISA (命令セットアーキテクチャ)、32ビット
WASI WebAssemblyシステムインターフェイス
Alpha Alphaアーキテクチャ
Alpha_SoftFloat AlphaソフトフロートABI
Alpha_HardFloat AlphaハードフロートABI
LittleEndian バイト順、最下位から
BigEndian バイト順、最上位から
ELFv1 実行可能形式とリンク可能形式v1
ELFv2 実行可能およびリンク可能フォーマットv2
D_BetterC ベターCコードとしてのD (コマンドラインスイッチ -betterC)が生成される
D_Exceptions 例外処理がサポートされている。 でコンパイルした場合、false に評価される。 コマンドラインスイッチ -betterC
D_ModuleInfo ModuleInfoがサポートされている。 コマンドラインスイッチでコンパイルする場合は、false に評価される。 に評価される。 -betterC
D_TypeInfo ランタイム型情報(TypeInfo )がサポートされている。 コマンドライン・スイッチでコンパイルする場合、false と評価される。 コマンド・ライン・スイッチ -betterC
D_Coverage コード・カバレッジ解析インスツルメンテーション (コマンドライン・スイッチ -cov)が生成される
D_Ddoc Ddocドキュメント (コマンドラインスイッチ -D)が生成されている
D_InlineAsm_X86 X86用インラインアセンブラが実装された
D_InlineAsm_X86_64 X86-64用インラインアセンブラが実装されている
D_LP64 Pointers 64ビットである (コマンドラインスイッチ -m64).(CのLP64モデルと混同しないこと)。
D_X32 ポインタは32ビットだが、ワードは64ビットのままである(x32 ABI)(X86_64 と並行して定義可能)。
D_HardFloat ターゲット・ハードウェアに浮動小数点ユニットがある
D_SoftFloat ターゲット・ハードウェアが浮動小数点ユニットを持たない
D_PIC 位置独立コード (コマンドラインスイッチ -fPIC)が生成されている
D_PIE 位置独立実行可能コード (コマンドラインスイッチ -fPIE)が生成される
D_SIMD ベクター拡張(__simd 経由)がサポートされている。
D_AVX AVXベクタ命令がサポートされている
D_AVX2 AVX2ベクター命令がサポートされている
D_Version2 これはDバージョン2コンパイラである
D_NoBoundsChecks 配列境界チェックは無効である。 (コマンドラインスイッチ -boundscheck=off)
D_ObjectiveC ターゲットはObjective-Cとのインターフェイスをサポートしている。
D_ProfileGC GC割り当てがプロファイリングされる (コマンドラインスイッチ -profile=gc)
D_Optimized 最適化を有効にしてコンパイルする (コマンドラインスイッチ -O)
Core 標準ランタイムのビルド時に定義される
Std 標準ライブラリのビルド時に定義される
unittest ユニットテストを有効にする (コマンドライン・スイッチ -unittest)
assert AssertExpressionsに対してチェックが行われる
D_PreConditions in契約に対してチェックが実行される
D_PostConditions out契約に対するチェックが発行される。
D_Invariants クラス不変量と 構造体不変量のチェックが発行される。
none コードの一部を無効にするために使用される。
all 常に定義されている。none

以下の識別子は定義されているが、非推奨である:

定義済みバージョン識別子(非推奨)
Version IdentifierDescription
darwinDarwinオペレーティング・システム。代わりにOSX
Thumb代わりにARM_Thumb を使用する。
S390XSystem/390Xアーキテクチャ、64ビット;SystemZ を代わりに使う。

その他は、理にかなったものや新しい実装が現れたときに追加される。

この言語の将来的な成長を可能にするためである、 で始まるバージョン識別子の名前空間は、D_を示す識別子のために予約されている。 で始まるバージョン識別子名前空間は、D言語仕様または新機能への適合を示す識別子用に予約されている。 または新機能への適合性を示す識別子のために予約されている。さらに さらに、上記の識別子に任意の文字を付加して派生したすべての識別子が予約されている。これは ARM_foo Windows_bar は予約されていない。 foo_ARMbar_Windows は予約されていない。

さらに、このリストにある定義済みのバージョン識別子は、コマンドラインやバージョン文から設定することはできない。 コマンドラインやバージョン文から設定することはできない。 (これにより、Windowslinux が同時に設定されることを防ぐ)。

コンパイラー・ベンダー固有のバージョンは、商標登録されているベンダー識別子の前に のように、商標登録されたベンダー識別子が接頭辞として付いていれば、コンパイラー・ベンダー固有のバージョンを事前に定義することができる:

version(DigitalMars_funky_extension)
{
    ...
}

正しいバージョン識別子を正しい目的に使うことが重要である。 重要である。例えば、ベンダー固有の機能を使用する場合は、ベンダー識別子を使用する。 を使う。オペレーティング・システム固有の機能を使う場合は、オペレーティング・システム識別子を使う。 オペレーティング・システム固有の機能を使用する場合は、オペレーティング・システム識別子を使用する。

デバッグ条件

DebugCondition:
    debug
    debug ( Identifier )

一般的に、2つのバージョンのプログラムがビルドされる、 リリースビルドとデバッグビルドである。 デバッグビルドには、余分なエラーチェックコードが含まれる、 テスト・ハーネス、プリティ・プリンティング・コードなどが含まれる。 デバッグ文は条件付きでコンパイルされる。 ステートメント本体で条件付きでコンパイルする。 これは、C言語では / 。 #ifdef DEBUG /#endif のペアを使用する。

-debug スイッチがコンパイラに渡されると、debug の条件が満たされる。 コンパイラに渡される。

debug ( 識別子 ) の条件は、デバッグ識別子が識別子と一致したときに満たされる。 条件が満たされる。

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

デバッグ・ステートメント

DebugConditionを持つConditionalStatementは、DebugStatementと呼ばれる。 呼ばれる。DebugStatementsは、セマンティック・チェックが緩和されている。 pure @nogcnothrow@safe のチェックは行われない。 DebugStatementsはpure,@nogcnothrow および@safe 属性の推論には影響しない。

未定義の振る舞い: これらのチェックは迂回されるので、コードが正しいかどうかはプログラマー次第である。 に任されている。例: 関数内で例外をスローすることは、未定義の動作である。 関数の中で例外を投げることは未定義の動作である。 nothrow
ベストプラクティス: これによって、デバッグ・ヘルプを提供するコードを簡単に挿入することができる、 これにより、デバッグ・ヘルプを提供するコードを簡単に挿入できるようになる。 DebugStatementsが有効になっているリリース・コードは決して出荷しないこと。

デバッグ仕様

DebugSpecification:
    debug = Identifier ;

デバッグ識別子は、コマンド・ライン・スイッチ -debug またはDebugSpecificationによって設定される。

デバッグ指定は、それが現れるモジュールにのみ影響し、インポートされたモジュールには影響しない。 インポートされたモジュールには影響しない。インポートされたモジュールには影響しない。 独自の名前空間にあり、バージョン識別子や他の シンボルとは独立している。

デバッグ指定を前方参照することは違法である:

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

DebugSpecificationsはモジュール・スコープでのみ使用できる。

DebugSpecificationsはモジュール・スコープにのみ出現する。 のパラメータでビルドできる:

debug(identifier) { } // debugキーワードが識別子の場合、デバッグコードを追加する

これらはおそらく、コマンドラインから と-debug=識別子で設定されると思われる。

静的if条件

StaticIfCondition:
    static if ( AssignExpression )

AssignExpressionは暗黙的にboolean型に変換される、 コンパイル時に評価される。 この条件は、true と評価されれば満たされる。 false と評価された場合は満たされない。

AssignExpressionが暗黙的にブーリアン型に変換できない場合、またはコンパイル時に評価される場合はエラーとなる。 に暗黙的に変換できない場合、またはコンパイル時に評価できない場合はエラーとなる。

StaticIfConditions は、モジュール、クラス、テンプレート、構造体、共用体、関数のスコープで使用できる。 関数スコープでは、シンボルが参照される。 関数スコープでは、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 Int = short;
    else
        static assert(0); // サポートされていない
}

Int!(32) a;  // aはintである
Int!(16) b;  // bはshortである
Int!(17) c;  // エラー、static assertトリップ

StaticIfConditionは、以下の点でIfStatementと異なる。 Ifステートメントとは以下の点で異なる:

  1. 宣言文を条件付きでコンパイルするために使用できる、 ステートメントだけでなく、宣言の条件付きコンパイルにも使用できる。
  2. たとえ{ } が条件付きコンパイルされた文に使われたとしても、新しいスコープを導入することはない。
  3. 条件を満たさない場合、条件付きでコンパイルされたコードは構文的に正しくなければならない。 は構文的に正しくなければならない。意味的に正しい必要はない。 意味的に正しい必要はない。
  4. コンパイル時に評価可能でなければならない。

静的Foreach

StaticForeach:
    static AggregateForeach
    static RangeForeach
StaticForeachDeclaration: StaticForeach DeclarationBlock StaticForeach : DeclDefsopt
StaticForeachStatement: StaticForeach NoScopeNonEmptyStatement

集計/範囲境界はコンパイル時に評価され、次のようになる。 コンパイル時に評価され、コンパイル時エンティティのシーケンスに変換される。 コンパイル時にForeachStatement/ForeachRangeStatementで対応するコードを評価することで、一連のコンパイル時エンティティに変換される。 で対応するコードを評価する。そして、static foreach の本体が、コンパイル時にForeachStatement/ForeachRangeStatementに対応する回数だけコピーされる。 シーケンスの要素数に対応する回数コピーされる。 コピーされる。i回目のコピーでは、変数名がi番目のエントリにバインドされる。static foreach 変数名は、変数宣言(定数の場合)または変数宣言(定数の場合)として、シーケンスのi番目のエントリにバインドされる。enum 変数宣言(定数の場合)またはalias 宣言(シンボルの場合)として、変数の名前がシーケンスのi番目のエントリに束縛される。特にstatic foreach 変数は決して実行時変数ではない)。

static foreach(i; [0, 1, 2, 3])
{
    pragma(msg, i);
}

static foreach 対応する 対応する 。(この場合 はタプルのコンパイル時シーケンスを生成し、その タプルはその後反復中にアンパックされる)。 foreach static foreach

static foreach(i, v; ['a', 'b', 'c', 'd'])
{
    static assert(i + 'a' == v);
}

ConditionalDeclarationsのボディと同様、ボディは新しいスコープを導入しない。static foreach ボディは新しいスコープを導入しない。したがって 宣言の生成に使用できる:

import std.range : iota;

static foreach(i; iota(0, 3))
{
    mixin(`enum x`, i, ` = i;`);
}

pragma(msg, x0, " ", x1," ", x2); // 0 1 2

関数の内部で、展開のたびに新しいスコープを作りたい場合、 別の中括弧を使う:

void fun()
{
    static foreach(s; ["hi", "hey", "hello"])
    {{
        enum len = s.length;    // 各繰り返しに対してローカルである
        static assert(len <= 5);
    }}

    static assert(!is(typeof(len)));
}

break とする。continue

static foreach はコード生成コンストラクトであり、ループではない。 ループではないので、breakcontinue を使って、その中の制御フローを変更することはできない。 を使うことはできない。適切な これは誤解を防ぐためである。 誤解を防ぐためである)。

int test(int x)
{
    int r = -1;
    switch(x)
    {
        static foreach(i; 0 .. 100)
        {
            case i:
                r = i;
                break; // エラー
        }
        default: break;
    }
    return r;
}

static foreach(i; 0 .. 200)
{
    static assert(test(i) == (i < 100 ? i : -1));
}

明示的なbreak/continue ラベルを使えば、この制限を回避できる。 この制限を避けることができる。(注釈:)static foreach 自体は、明示的にラベルを付けても、破ることも続けることもできない。 それ自体は、たとえ明示的にラベル付けされていても、壊すことも続けることもできないことに注意されたい)。 ラベルを付けても、 自体は壊すことも続けることもできないことに注意されたい)。

int test(int x)
{
    int r = -1;
    Lswitch: switch(x)
    {
        static foreach(i; 0 .. 100)
        {
            case i:
                r = i;
                break Lswitch;
        }
        default: break;
    }
    return r;
}

static foreach(i; 0 .. 200)
{
    static assert(test(i) == (i<100 ? i : -1));
}

static assert

StaticAssert:
    static assert ( ArgumentList ) ;

最初のAssignExpressionはコンパイル時に評価され、ブール値に変換される。 をブール値に変換する。その値が真であれば、"static assert" は無視される。 は無視される。値が偽の場合、エラー診断が発行され、コンパイルは失敗する。 が発行され、コンパイルは失敗する。

失敗した場合、後続のAssignExpressionsはそれぞれ文字列に変換され、連結される。 はそれぞれ文字列に変換され、連結される。結果の文字列は はエラー診断とともに出力される。

AssertExpressionsとは異なり、StaticAssertsは常にコンパイラによってチェックされ、評価される。 によってチェックされ、評価される。 を満たさない限り、コンパイラによって常にチェックされ、評価される。

void foo()
{
    if (0)
    {
        assert(0);  // 決してトリップしない
        static assert(0); // 常にトリップする
    }
    version (BAR)
    {
    }
    else
    {
        static assert(0); // バージョンBARが定義されていないときにトリップする
    }
}

StaticAssertは、コードでサポートされていない条件構成に注意を促すのに便利なツールだ。 コードでサポートされていない