Ddoc
$(SPEC_S Conditional Compilation,
$(P $(I Conditional compilation) is the process of selecting which
code to compile and which code to not compile.
(In C and C++, conditional compilation is done with the preprocessor
directives $(CODE #if) / $(CODE #else) / $(CODE #endif).)
)
$(GRAMMAR
$(GNAME ConditionalDeclaration):
$(GLINK Condition) $(I CCDeclarationBlock)
$(GLINK Condition) $(I CCDeclarationBlock) $(B else) $(I CCDeclarationBlock)
$(GLINK Condition) $(B :) $(I Declarations)
$(GNAME CCDeclarationBlock):
$(LINK2 declaration.html#Declaration, $(I Declaration))
$(B {) $(I Declarations) $(B })
$(B { })
$(GNAME Declarations):
$(LINK2 declaration.html#Declaration, $(I Declaration))
$(LINK2 declaration.html#Declaration, $(I Declaration)) $(I Declarations)
$(GNAME ConditionalStatement):
$(GLINK Condition) $(LINK2 statement.html#NoScopeNonEmptyStatement, $(I NoScopeNonEmptyStatement))
$(GLINK Condition) $(LINK2 statement.html#NoScopeNonEmptyStatement, $(I NoScopeNonEmptyStatement)) $(B else) $(LINK2 statement.html#NoScopeNonEmptyStatement, $(I NoScopeNonEmptyStatement))
)
$(P If the $(GLINK Condition) is satisfied, then the following
$(I CCDeclarationBlock) or $(I Statement) is compiled in.
If it is not satisfied, the $(I CCDeclarationBlock) or $(I Statement)
after the optional $(CODE else) is compiled in.
)
$(P Any $(I CCDeclarationBlock) or $(I Statement) that is not
compiled in still must be syntactically correct.
)
$(P No new scope is introduced, even if the
$(I CCDeclarationBlock) or $(I Statement)
is enclosed by $(CODE { }).
)
$(P $(I ConditionalDeclaration)s and $(I ConditionalStatement)s
can be nested.
)
$(P The $(LINK2 #StaticAssert, $(I StaticAssert)) can be used
to issue errors at compilation time for branches of the conditional
compilation that are errors.
)
$(P $(I Condition) comes in the following forms:
)
$(GRAMMAR
$(GNAME Condition):
$(LINK2 #version, $(I VersionCondition))
$(LINK2 #debug, $(I DebugCondition))
$(LINK2 #staticif, $(I StaticIfCondition))
)
$(LNAME2 version, Version Condition)
$(P Versions enable multiple versions of a module to be implemented
with a single source file.
)
$(GRAMMAR
$(GNAME VersionCondition):
$(B version $(LPAREN)) $(LINK2 lex.html#IntegerLiteral, $(I IntegerLiteral)) $(B $(RPAREN))
$(B version $(LPAREN)) $(I Identifier) $(B $(RPAREN))
$(V2
$(B version $(LPAREN)) $(B unittest) $(B $(RPAREN)))
)
$(P The $(I VersionCondition) is satisfied if the $(I IntegerLiteral)
is greater than or equal to the current $(I version level),
or if $(I Identifier) matches a $(I version identifier).
)
$(P The $(I version level) and $(I version identifier) can
be set on the command line by the $(B -version) switch
or in the module itself with a $(GLINK VersionSpecification),
or they can be predefined by the compiler.
)
$(P 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
}
------
$(V2 $(P The $(B version(unittest)) is satisfied if and only if the code is
compiled with unit tests enabled (the $(TT -unittest) option on $(B dmd)).
)
)
$(LNAME2 VersionSpecification, Version Specification)
$(GRAMMAR
$(GNAME VersionSpecification):
$(B version =) $(I Identifier) $(B ;)
$(B version =) $(LINK2 lex.html#IntegerLiteral, $(I IntegerLiteral)) $(B ;)
)
$(P 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 ...
}
------
$(P Version identifiers or levels may not be forward referenced:
)
------
version (Foo)
{
int x;
}
version = Foo; // error, Foo already used
------
$(P $(I VersionSpecification)s may only appear at module scope.)
$(P 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.
)
$(P Here's an example of a $(I full) version as opposed to
a $(I demo) 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($(I n)) // add in version code if version level is >= $(I n)
{
... version code ...
}
version($(I identifier)) // add in version code if version
// keyword is $(I identifier)
{
... version code ...
}
------
$(P These are presumably set by the command line as
$(TT -version=$(I n)) and $(TT -version=$(I identifier)).
)
$(P 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.
)
$(TABLE2 Predefined Version Identifiers,
$(TR $(TH Version Identifier) $(TH Description))
$(TR $(TD $(B DigitalMars)) $(TD Digital Mars is the compiler vendor))
$(TR $(TD $(B X86)) $(TD Intel and AMD 32 bit processors))
$(TR $(TD $(B X86_64)) $(TD AMD and Intel 64 bit processors))
$(TR $(TD $(B Windows)) $(TD Microsoft Windows systems))
$(TR $(TD $(B Win32)) $(TD Microsoft 32 bit Windows systems))
$(TR $(TD $(B Win64)) $(TD Microsoft 64 bit Windows systems))
$(TR $(TD $(B linux)) $(TD All linux systems))
$(TR $(TD $(B D_NET)) $(TD .NET))
$(TR $(TD $(B OSX)) $(TD Mac OS X))
$(TR $(TD $(B FreeBSD)) $(TD FreeBSD))
$(TR $(TD $(B Solaris)) $(TD Solaris))
$(V2
$(TR $(TD $(B Posix)) $(TD All posix systems (includes Linux, FreeBSD, OS X, Solaris, etc.)))
)
$(TR $(TD $(B LittleEndian)) $(TD Byte order, least significant first))
$(TR $(TD $(B BigEndian)) $(TD Byte order, most significant first))
$(TR $(TD $(B D_Coverage)) $(TD $(LINK2 code_coverage.html, Code coverage analysis) instrumentation (command line $(LINK2 dmd-windows.html#switches, switch) $(B -cov)) is being generated))
$(TR $(TD $(B D_Ddoc)) $(TD $(LINK2 ddoc.html, Ddoc) documentation (command line $(LINK2 dmd-windows.html#switches, switch) $(B -D)) is being generated))
$(TR $(TD $(B D_InlineAsm_X86)) $(TD $(LINK2 iasm.html, Inline assembler) for X86 is implemented))
$(TR $(TD $(B D_InlineAsm_X86_64)) $(TD $(LINK2 iasm.html, Inline assembler) for X86-64 is implemented))
$(TR $(TD $(B D_LP64)) $(TD Pointers are 64 bits (command line $(LINK2 dmd-windows.html#switches, switch) $(B -m64))))
$(TR $(TD $(B D_PIC)) $(TD Position Independent Code (command line $(LINK2 dmd-windows.html#switches, switch) $(B -fPIC)) is being generated))
$(V2 $(TR $(TD $(B unittest)) $(TD $(LINK2 unittest.html, Unit tests) are enabled (command line $(LINK2 dmd-windows.html#switches, switch) $(B -unittest)))))
$(V2 $(TR $(TD $(B D_Version2)) $(TD This is a D version 2 compiler)))
$(TR $(TD $(B none)) $(TD Never defined; used to just disable a section of code))
$(TR $(TD $(B all)) $(TD Always defined; used as the opposite of $(B none)))
)
$(P Others will be added as they make sense and new implementations appear.
)
$(P It is inevitable that the D language will evolve over time.
Therefore, the version identifier namespace beginning with "D_"
is reserved for identifiers indicating D language specification
or new feature conformance.
)
$(P Furthermore, predefined version identifiers from this list cannot
be set from the command line or from version statements.
(This prevents things like both $(B Windows) and $(B linux)
being simultaneously set.)
)
$(P Compiler vendor specific versions can be predefined if the
trademarked vendor
identifier prefixes it, as in:
)
------
version(DigitalMars_funky_extension)
{
...
}
------
$(P 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.
)
$(LNAME2 debug, Debug Condition)
$(P 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 $(CODE #ifdef DEBUG) / $(CODE #endif) pairs.
)
$(GRAMMAR
$(GNAME DebugCondition):
$(B debug)
$(B debug $(LPAREN)) $(LINK2 lex.html#IntegerLiteral, $(I IntegerLiteral)) $(B $(RPAREN))
$(B debug $(LPAREN)) $(I Identifier) $(B $(RPAREN))
)
$(P The $(B debug) condition is satisfied when the $(B -debug) switch is
thrown on the compiler.
)
$(P The $(B debug $(LPAREN)) $(I IntegerLiteral) $(B $(RPAREN)) condition is satisfied
when the debug
level is >= $(I IntegerLiteral).
)
$(P The $(B debug $(LPAREN)) $(I Identifier) $(B $(RPAREN)) condition is satisfied
when the debug identifier matches $(I Identifier).
)
------
class Foo
{
int a, b;
debug:
int flag;
}
------
Debug Specification
$(GRAMMAR
$(GNAME DebugSpecification):
$(B debug =) $(I Identifier) $(B ;)
$(B debug =) $(LINK2 lex.html#IntegerLiteral, $(I IntegerLiteral)) $(B ;)
)
$(P Debug identifiers and levels are set either by the command line switch
$(B -debug) or by a $(I DebugSpecification).
)
$(P 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.
)
$(P It is illegal to forward reference a debug specification:
)
------
debug (foo) writefln("Foo");
debug = foo; // error, foo used before set
------
$(P $(I DebugSpecification)s may only appear at module scope.)
$(P Various different debug builds can be built with a parameter to
debug:
)
------
debug($(LINK2 lex.html#IntegerLiteral, $(I IntegerLiteral))) { } // add in debug code if debug level is >= $(LINK2 lex.html#IntegerLiteral, $(I IntegerLiteral))
debug($(I identifier)) { } // add in debug code if debug keyword is $(I identifier)
------
$(P These are presumably set by the command line as
$(TT -debug=$(I n)) and $(TT -debug=$(I identifier)).
)
$(LNAME2 staticif, Static If Condition)
$(GRAMMAR
$(GNAME StaticIfCondition):
$(B static if $(LPAREN)) $(ASSIGNEXPRESSION) $(B $(RPAREN))
)
$(P $(ASSIGNEXPRESSION) is implicitly converted to a boolean type,
and is evaluated at compile time.
The condition is satisfied if it evaluates to $(B true).
It is not satisfied if it evaluates to $(B false).
)
$(P It is an error if $(ASSIGNEXPRESSION) cannot be implicitly converted
to a boolean type or if it cannot be evaluated at compile time.
)
$(P $(I StaticIfCondition)s
can appear in module, class, template, struct, union, or function scope.
In function scope, the symbols referred to in the
$(ASSIGNEXPRESSION) can be any that can normally be referenced
by an expression at that point.
)
------
const int i = 3;
int j = 4;
$(B static if) (i == 3) // ok, at module scope
int x;
class C
{ const int k = 5;
$(B static if) (i == 3) // ok
int x;
$(B else)
long x;
$(B static if) (j == 3) // error, j is not a constant
int y;
$(B static if) (k == 5) // ok, k is in current scope
int z;
}
template INT(int i)
{
$(B static if) (i == 32)
alias int INT;
$(B else static if) (i == 16)
alias short INT;
$(B else)
static assert(0); // not supported
}
INT!(32) a; // a is an int
INT!(16) b; // b is a short
INT!(17) c; // error, static assert trips
------
$(P A $(I StaticIfConditional) condition differs from an
$(I IfStatement) in the following ways:
)
$(OL
$(LI It can be used to conditionally compile declarations,
not just statements.
)
$(LI It does not introduce a new scope even if $(B { })
are used for conditionally compiled statements.
)
$(LI For unsatisfied conditions, the conditionally compiled code
need only be syntactically correct. It does not have to be
semantically correct.
)
$(LI It must be evaluatable at compile time.
)
)
$(LNAME2 static-assert, $(LNAME2 StaticAssert, Static Assert))
$(GRAMMAR
$(GNAME StaticAssert):
$(B static assert $(LPAREN)) $(ASSIGNEXPRESSION) $(B $(RPAREN);)
$(B static assert $(LPAREN)) $(ASSIGNEXPRESSION) $(B ,) $(ASSIGNEXPRESSION) $(B $(RPAREN);)
)
$(P $(ASSIGNEXPRESSION) 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.
)
$(P Unlike $(I AssertExpression)s, $(I StaticAssert)s are always
checked and evaluted by the compiler unless they appear in an
unsatisfied conditional.
)
------
void foo()
{
if (0)
{
assert(0); // never trips
static assert(0); // always trips
}
version (BAR)
{
}
else
{
static assert(0); // trips when version BAR is not defined
}
}
------
$(P $(I StaticAssert) is useful tool for drawing attention to conditional
configurations not supported in the code.
)
$(P The optional second $(ASSIGNEXPRESSION) can be used to supply
additional information, such as a text string, that will be
printed out along with the error diagnostic.
)
)
Macros:
TITLE=Conditional Compilation
WIKI=Version