Conditional compilation is the process of selecting which code to compile and which code to not compile.
ConditionalDeclaration:ConditionDeclarationBlockConditionDeclarationBlockelseDeclarationBlockCondition:DeclDefsoptConditionDeclarationBlockelse:DeclDefsoptConditionalStatement:ConditionNoScopeNonEmptyStatementConditionNoScopeNonEmptyStatementelseNoScopeNonEmptyStatement
If theCondition is satisfied, then the followingDeclarationBlock orStatement is compiled in. If it is not satisfied, theDeclarationBlock orStatement after the optionalelse is compiled in.
AnyDeclarationBlock orStatement that is not compiled in still must be syntactically correct.
No new scope is introduced, even if theDeclarationBlock orStatement is enclosed by{ }.
ConditionalDeclarations andConditionalStatements can be nested.
TheStaticAssert can be used to issue errors at compilation time for branches of the conditional compilation that are errors.
Condition comes in the following forms:
Condition:VersionConditionDebugConditionStaticIfCondition
VersionCondition:version (Identifier)version (unittest)version (assert)
Versions enable multiple versions of a module to be implemented with a single source file.
TheVersionCondition is satisfied ifIdentifier matches aversion identifier.
Theversion identifier can be set on the command line by the-version switch or in the module itself with aVersionSpecification, or they can be predefined by the compiler.
Version identifiers are in their own unique name space, they do not conflict with debug identifiers or other symbols in the module. Version identifiers defined in one module have no influence over other imported modules.
int k;version (Demo)// compile in this code block for the demo version{int i;int k;// error, k already defined i = 3;}x = i;// uses the i declared above
version (X86){ ...// implement custom inline assembler version}else{ ...// use default, but slow, version}
Theversion(unittest) is satisfied if and only if the code is compiled with unit tests enabled (the-unittest option ondmd).
VersionSpecification:version =Identifier;
The version specification makes it straightforward to group a set of features under one major version, for example:
version (ProfessionalEdition){version = FeatureA;version = FeatureB;version = FeatureC;}version (HomeEdition){version = FeatureA;}...version (FeatureB){ ... implement Feature B ...}
Version identifiers or levels may not be forward referenced:
version (Foo){int x;}version = Foo;// error, Foo already used
VersionSpecifications may only appear at module scope.
While the debug and version conditions superficially behave the same, they are intended for very different purposes. Debug statements are for adding debug code that is removed for the release version. Version statements are to aid in portability and multiple release versions.
Here's an example of afull version as opposed to ademo version:
class Foo{int a, b;version(full) {int extrafunctionality() { ...return 1;// extra functionality is supported } }else// demo {int extrafunctionality() {return 0;// extra functionality is not supported } }}
Various different version builds can be built with a parameter to version:
version(identifier)// add in version code if version// keyword is identifier{ ...version code ...}
This is presumably set by the command line as-version=identifier.
Several environmental version identifiers and identifier name spaces are predefined for consistent usage. Version identifiers do not conflict with other identifiers in the code, they are in a separate name space. Predefined version identifiers are global, i.e. they apply to all modules being compiled and imported.
| Version Identifier | Description |
|---|---|
| Host Compiler | |
| DigitalMars | DMD (Digital Mars D) |
| GNU | GDC (GNU D Compiler) |
| LDC | LDC (LLVM D Compiler) |
| SDC | SDC (Snazzy D Compiler) |
| Target Operating System | |
| Windows | Microsoft Windows systems Win32 and Win64 |
| Win32 | Microsoft 32-bit Windows systems |
| Win64 | Microsoft 64-bit Windows systems |
| linux | All Linux systems |
| Apple | Apple systems OSX, iOS, TVOS, WatchOS and VisionOS |
| OSX | macOS |
| iOS | iOS |
| TVOS | tvOS |
| WatchOS | watchOS |
| VisionOS | visionOS |
| FreeBSD | FreeBSD |
| OpenBSD | OpenBSD |
| NetBSD | NetBSD |
| DragonFlyBSD | DragonFlyBSD |
| BSD | All other BSDs |
| Solaris | Solaris |
| Posix | All POSIX systems (includes Linux, FreeBSD, OS X, Solaris, etc.) |
| AIX | IBM Advanced Interactive eXecutive OS |
| Haiku | The Haiku operating system |
| SkyOS | The SkyOS operating system |
| SysV3 | System V Release 3 |
| SysV4 | System V Release 4 |
| Hurd | GNU Hurd |
| Android | The Android platform |
| Emscripten | The Emscripten platform |
| PlayStation | The PlayStation platform |
| PlayStation4 | The PlayStation 4 platform |
| FreeStanding | An environment without an operating system (such as Bare-metal targets) |
| Target Environment | |
| Cygwin | The Cygwin environment |
| MinGW | The MinGW environment |
| CRuntime_Bionic | Bionic C runtime |
| CRuntime_DigitalMars | DigitalMars C runtime |
| CRuntime_Glibc | Glibc C runtime |
| CRuntime_Microsoft | Microsoft C runtime |
| CRuntime_Musl | musl C runtime |
| CRuntime_Newlib | newlib C runtime |
| CRuntime_UClibc | uClibc C runtime |
| CRuntime_WASI | WASI C runtime |
| CppRuntime_Clang | Deprecated, useCppRuntime_LLVM instead |
| CppRuntime_DigitalMars | DigitalMars C++ runtime |
| CppRuntime_Gcc | Deprecated, useCppRuntime_GNU instead |
| CppRuntime_LLVM | LLVM libc++ C++ runtime |
| CppRuntime_GNU | GNU libstdc++ C++ runtime |
| CppRuntime_Microsoft | Microsoft C++ runtime |
| CppRuntime_Sun | Sun C++ runtime |
| Target CPU and Instruction Set | |
| X86 | Intel and AMD 32-bit processors |
| X86_64 | Intel and AMD 64-bit processors |
| ARM | The ARM architecture (32-bit) (AArch32 et al) |
| ARM_Thumb | ARM in any Thumb mode |
| ARM_SoftFloat | The ARMsoft floating point ABI |
| ARM_SoftFP | The ARMsoftfp floating point ABI |
| ARM_HardFloat | The ARMhardfp floating point ABI |
| AArch64 | The Advanced RISC Machine architecture (64-bit) |
| AsmJS | The asm.js intermediate programming language |
| AVR | 8-bit Atmel AVR Microcontrollers |
| Epiphany | The Epiphany architecture |
| PPC | The PowerPC architecture, 32-bit |
| PPC_SoftFloat | The PowerPC soft float ABI |
| PPC_HardFloat | The PowerPC hard float ABI |
| PPC64 | The PowerPC architecture, 64-bit |
| IA64 | The Itanium architecture (64-bit) |
| MIPS32 | The MIPS architecture, 32-bit |
| MIPS64 | The MIPS architecture, 64-bit |
| MIPS_O32 | The MIPS O32 ABI |
| MIPS_N32 | The MIPS N32 ABI |
| MIPS_O64 | The MIPS O64 ABI |
| MIPS_N64 | The MIPS N64 ABI |
| MIPS_EABI | The MIPS EABI |
| MIPS_SoftFloat | The MIPSsoft-float ABI |
| MIPS_HardFloat | The MIPShard-float ABI |
| MSP430 | The MSP430 architecture |
| NVPTX | The Nvidia Parallel Thread Execution (PTX) architecture, 32-bit |
| NVPTX64 | The Nvidia Parallel Thread Execution (PTX) architecture, 64-bit |
| RISCV32 | The RISC-V architecture, 32-bit |
| RISCV64 | The RISC-V architecture, 64-bit |
| SPARC | The SPARC architecture, 32-bit |
| SPARC_V8Plus | The SPARC v8+ ABI |
| SPARC_SoftFloat | The SPARC soft float ABI |
| SPARC_HardFloat | The SPARC hard float ABI |
| SPARC64 | The SPARC architecture, 64-bit |
| S390 | The System/390 architecture, 32-bit |
| SystemZ | The System Z architecture, 64-bit |
| HPPA | The HP PA-RISC architecture, 32-bit |
| HPPA64 | The HP PA-RISC architecture, 64-bit |
| SH | The SuperH architecture, 32-bit |
| WebAssembly | The WebAssembly virtual ISA (instruction set architecture), 32-bit |
| WASI | The WebAssembly System Interface |
| Xtensa | The Xtensa Architecture, 32-bit |
| Alpha | The Alpha architecture |
| Alpha_SoftFloat | The Alpha soft float ABI |
| Alpha_HardFloat | The Alpha hard float ABI |
| Byte Order (endianess) | |
| LittleEndian | Byte order, least significant first |
| BigEndian | Byte order, most significant first |
| Executable and Link Format | |
| ELFv1 | Elf version 1 |
| ELFv2 | Elf version 2 |
| Miscellaneous | |
| D_BetterC | D as Better C code (command line switch-betterC) is being generated |
| D_Exceptions | Exception handling is supported. Evaluates tofalse when compiling with command line switch-betterC |
| D_ModuleInfo | ModuleInfo is supported. Evaluates tofalse when compiling with command line switch-betterC |
| D_TypeInfo | Runtime type information (a.k.aTypeInfo) is supported. Evaluates tofalse when compiling with command line switch-betterC |
| D_Coverage | Code coverage analysis instrumentation (command line switch-cov) is being generated |
| D_Ddoc | Ddoc documentation (command line switch-D) is being generated |
| D_InlineAsm_X86 | Inline assembler for X86 is implemented |
| D_InlineAsm_X86_64 | Inline assembler for X86-64 is implemented |
| D_LP64 | Pointers are 64 bits (command line switch-m64). (Do not confuse this with C's LP64 model) |
| D_X32 | Pointers are 32 bits, but words are still 64 bits (x32 ABI) (This can be defined in parallel toX86_64) |
| D_HardFloat | The target hardware has a floating-point unit |
| D_SoftFloat | The target hardware does not have a floating-point unit |
| D_PIC | Position Independent Code (command line switch-fPIC) is being generated |
| D_PIE | Position Independent Executable (command line switch-fPIE) is being generated |
| D_SIMD | Vector extensions (via__simd) are supported |
| D_AVX | AVX Vector instructions are supported |
| D_AVX2 | AVX2 Vector instructions are supported |
| D_Version2 | This is a D version 2 compiler |
| D_NoBoundsChecks | Array bounds checks are disabled (command line switch-boundscheck=off) |
| D_ObjectiveC | The target supports interfacing with Objective-C |
| D_ProfileGC | GC allocations being profiled (command line switch-profile=gc) |
| D_Optimized | Compiling with enabled optimizations (command line switch-O) |
| Core | Defined when building the standard runtime |
| Std | Defined when building the standard library |
| unittest | Unit tests are enabled (command line switch-unittest) |
| assert | Checks are being emitted forAssertExpressions |
| D_PreConditions | Checks are being emitted forin contracts |
| D_PostConditions | Checks are being emitted forout contracts |
| D_Invariants | Checks are being emitted forclass invariants andstruct invariants |
| Special Cases | |
| none | Never defined; used to just disable a section of code |
| all | Always defined; used as the opposite ofnone |
The following identifiers are defined, but are deprecated:
| Version Identifier | Description |
|---|---|
| darwin | The Darwin operating system; useOSX instead |
| Thumb | ARM in Thumb mode; useARM_Thumb instead |
| S390X | The System/390X architecture, 64-bit; useSystemZ instead |
Others will be added as they make sense and new implementations appear.
To allow for future growth of the language, the version identifier namespace beginning with "D_" is reserved for identifiers indicating D language specification or new feature conformance. Further, all identifiers derived from the ones listed above by appending any character(s) are reserved. This means that e.g.ARM_foo andWindows_bar are reserved whilefoo_ARM andbar_Windows are not.
Predefined version identifiers from this list cannot be set from the command line or from version statements. (This prevents things like bothWindows andlinux being simultaneously set.)
Compiler vendor specific versions can be predefined if the trademarked vendor identifier prefixes it, as in:
version(DigitalMars_funky_extension){ ...}It is important to use the right version identifier for the right purpose. For example, use the vendor identifier when using a vendor specific feature. Use the operating system identifier when using an operating system specific feature, etc.
DebugCondition:debugdebug (Identifier)
Two versions of programs are commonly built, a release build and a debug build. The debug build includes extra error checking code, test harnesses, pretty-printing code, etc. The debug statement conditionally compiles in its statement body. It is D's way of what in C is done with#ifdef DEBUG /#endif pairs.
Thedebug condition is satisfied when the-debug switch is passed to the compiler.
Thedebug (Identifier) condition is satisfied when the debug identifier matchesIdentifier.
class Foo{int a, b;debug:int flag;}
AConditionalStatement that has aDebugCondition is called aDebugStatement.DebugStatements have relaxed semantic checks in thatpure,@nogc,nothrow and@safe checks are not done. Neither doDebugStatements influence the inference ofpure,@nogc,nothrow and@safe attributes.
DebugSpecification:debug =Identifier;
Debug identifiers are set either by the command line switch-debug or by aDebugSpecification.
Debug specifications only affect the module they appear in, they do not affect any imported modules. Debug identifiers are in their own namespace, independent from version identifiers and other symbols.
It is illegal to forward reference a debug specification:
debug(foo) writeln("Foo");debug = foo;// error, foo used before set
DebugSpecifications may only appear at module scope.
Various different debug builds can be built with a parameter to debug:
debug(identifier) { }// add in debug code if debug keyword is identifier
These are presumably set by the command line as and-debug=identifier.
StaticIfCondition:static if (AssignExpression)
AssignExpression is implicitly converted to a boolean type, and is evaluated at compile time. The condition is satisfied if it evaluates totrue. It is not satisfied if it evaluates tofalse.
It is an error ifAssignExpression cannot be implicitly converted to a boolean type or if it cannot be evaluated at compile time.
StaticIfConditions can appear in module, class, template, struct, union, or function scope. In function scope, the symbols referred to in theAssignExpression can be any that can normally be referenced by an expression at that point.
constint i = 3;int j = 4;staticif (i == 3)// ok, at module scopeint x;class C{constint k = 5;staticif (i == 3)// okint x;elselong x;staticif (j == 3)// error, j is not a constantint y;staticif (k == 5)// ok, k is in current scopeint z;}
template Int(int i){staticif (i == 32)alias Int =int;elsestaticif (i == 16)alias Int =short;elsestaticassert(0);// not supported}Int!(32) a;// a is an intInt!(16) b;// b is a shortInt!(17) c;// error, static assert trips
AStaticIfCondition differs from anIfStatement in the following ways:
StaticForeachDeclaration:StaticForeachDeclarationBlockStaticForeach:DeclDefsoptStaticForeachStatement:StaticForeachNoScopeNonEmptyStatementStaticForeach:staticAggregateForeachstaticRangeForeach
static foreach expands itsDeclarationBlock orDeclDefs into a series of declarations, each of which may reference anyForeachType symbols declared.
staticforeach (i; [0, 1, 2, 3]){pragma(msg, i);}
static foreach supports multipleForeachType variables in cases where the correspondingforeach statement supports them. (In this case,static foreach generates a compile-time sequence of tuples, and the tuples are subsequently unpacked during iteration).
staticforeach (i, v; ['a', 'b', 'c', 'd']){staticassert(i + 'a' == v);}
Like bodies ofConditionalDeclarations, astatic foreach body does not introduce a new scope. Therefore, it can be used to add declarations to an existing scope:
import std.range : iota;staticforeach (i; iota(0, 3)){mixin(`enum x`, i,` = i;`);}pragma(msg, x0," ", x1," ", x2);// 0 1 2
Inside a function, if a new scope is desired for each expansion, use another set of braces:
void fun(){staticforeach (s; ["hi","hey","hello"]) {{enum len = s.length;// local to each iterationstaticassert(len <= 5); }}staticassert(!is(typeof(len)));}
static foreach supports sequence expansionlikeforeach.
Asstatic foreach is a code generation construct and not a loop,break andcontinue cannot be used to change control flow within it. Instead of breaking or continuing a suitable enclosing statement, such an usage yields an error (this is to prevent misunderstandings).
int test(int x){int r = -1;switch(x) {staticforeach (i; 0 .. 5) {case i: r = i;break;// error }default:break; }return r;}staticforeach (i; 0 .. 10){staticassert(test(i) == (i < 5 ? i : -1));}
An explicitbreak/continue label can be used to avoid this limitation. (Note thatstatic foreach itself cannot be broken nor continued even if it is explicitly labeled.)
int test(int x){int r = -1; Lswitch:switch(x) {staticforeach (i; 0 .. 5) {case i: r = i;break Lswitch; }default:break; }return r;}staticforeach (i; 0 .. 10){staticassert(test(i) == (i < 5 ? i : -1));}
StaticAssert:static assert (ArgumentList) ;
The firstAssignExpression is evaluated at compile time, and converted to a boolean value. If the value is true, the static assert is ignored. If the value is false, an error diagnostic is issued and the compile fails.
On failure, any subsequentAssignExpressions will each be converted to string and then concatenated. The resulting string will be printed out along with the error diagnostic.
UnlikeAssertExpressions,StaticAsserts are always checked and evaluated by the compiler unless they appear in an unsatisfied conditional.
void foo(){if (0) {assert(0);// never tripsstaticassert(0);// always trips }version (BAR) { }else {staticassert(0);// trips when version BAR is not defined }}
StaticAssert is useful tool for drawing attention to conditional configurations not supported in the code.