www.digitalmars.com [Home] [Search] [D]
Last modified Dec 10, 2003.

Debug, Version, and Static Assert

D supports building multiple versions and various debug builds from the same source code using the features:
	DebugSpecification
	DebugAttribute
	DebugStatement

	VersionSpecification
	VersionAttribute
	VersionStatement

	StaticAssert
	

Predefined Versions

Several environmental version identifiers and identifier name spaces are predefined to encourage consistent usage. Version identifiers do not conflict with other identifiers in the code, they are in a separate name space.
DigitalMars
Digital Mars is the compiler vendor
X86
Intel and AMD 32 bit processors
AMD64
AMD 64 bit processors
Windows
Microsoft Windows systems
Win32
Microsoft 32 bit Windows systems
Win64
Microsoft 64 bit Windows systems
linux
All linux systems
LittleEndian
Byte order, least significant first
BigEndian
Byte order, most significant first
D_InlineAsm
Inline assembler is implemented
none
Never defined; used to just disable a section of code
Others will be added as they make sense and new implementations appear.

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.

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.

Specification

	DebugSpecification
	    debug = Identifier ;
	    debug = Integer ;

	VersionSpecification
	    version = Identifier ;
	    version = Integer ;
	
Version specifications do not declare any symbols, but instead set a version in the same manner that the -version does on the command line. The version specification is used for conditional compilation with version attributes and version statements.

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 ...
	}
	

Debug Statement

Two versions of programs are commonly built, a release build and a debug build. The debug build commonly 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.
	DebugStatement:
		debug Statement
		debug ( Integer ) Statement
		debug ( Identifier ) Statement
	
Debug statements are compiled in when the -debug switch is thrown on the compiler.

debug(Integer) statements are compiled in when the debug level n set by the -debug(n) switch is <= Integer.

debug(Identifier) statements are compiled in when the debug identifier set by the -debug(identifier) matches Identifier.

If Statement is a block statement, it does not introduce a new scope. For example:

	int k;
	debug
	{   int i;
	    int k;	// error, k already defined

	    i = 3;
	}
	x = i;		// uses the i declared above
	
There is no else clause for a debug statement, as debug statements should add code, not subtract code.

Version Statement

It is commonplace to conveniently support multiple versions of a module with a single source file. While the D way is to isolate all versioning into separate modules, that can get burdensome if it's just simple line change, or if the entire program would otherwise fit into one module.

	VersionStatement:
		VersionPredicate Statement
		VersionPredicate Statement else Statement

	VersionPredicate
		version ( Integer )
		version ( Identifier )
	
The version statement conditionally compiles in its statement body based on the version specified by the Integer of Identifier. Both forms are set by the -version switch to the compiler. If Statement is a block statement, it does not introduce a new scope. For example:
	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
	
The version statement works together with the version attribute for declarations.

Version statements can nest.

The optional else clause gets conditionally compiled in if the version predicate is false:

	version (X86)
	{
	    ... // implement custom inline assembler version
	}
	else
	{
	    ... // use default, but slow, version
	}
	
While the debug and version statements 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.

Debug Attribute

	DebugAttribute:
	    debug
	    debug ( Integer )
	    debug ( 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 attribute conditionally compiles in code:
	class Foo
	{
		int a, b;
	    debug:
		int flag;
	}
	
Conditional Compilation means that if the code is not compiled in, it still must be syntactically correct, but no semantic checking or processing is done on it. No symbols are defined, no typechecking is done, no code is generated, no imports are imported. Various different debug builds can be built with a parameter to debug:
	debug(n) { }	// add in debug code if debug level is <= n
	debug(identifier) { } // add in debug code if debug keyword is identifier
	
These are presumably set by the command line as -debug=n and -debug=identifier.

Version Attribute

	VersionAttribute:
	    version ( Integer )
	    version ( Identifier )
	
The version attribute is very similar to the debug attribute, and in many ways is functionally interchangeable with it. The purpose of it, however, is different. While debug is for building debugging versions of a program, version is for using the same source to build multiple release versions.

For instance, there may be a full version as opposed to a 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(n) { }	// add in version code if version level is >= n
	version(identifier) { } // add in version code if version keyword is identifier
	
These are presumably set by the command line as -version=n and -version=identifier.

Static Assert

	StaticAssert:
	    static assert ( Expression );
	
Expression 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.

Unlike AssertExpressions, StaticAsserts are always checked and evaluted by the compiler unless they appear in a false debug or version conditional.

	void foo()
	{
	    if (0)
	    {
		assert(0);		// never trips
		static assert(0);	// always trips
	    }
	    version (BAR)
	    {
		static assert(0);	// does not trip unless BAR is defined
	    }
	}
	

Copyright (c) 1999-2002 by Digital Mars, All Rights Reserved