Conditional compilation

The C preprocessor makes it possible to conditionally compile certain blocks of code using #define and related directives. Once again, D achieves similar results using built-in compile-time statements, such as version, debug, and static if.

The version condition

A version condition is used to instruct the compiler to generate code for anything in the version block only if the specific condition is defined. Here's an example:

version(Windows)
  pragma(msg, "We are compiling on Windows.");
else version(OSX)
  pragma(msg, "We are compiling on a Mac OS X system.");
else version(Posix)
  pragma(msg, "We are compiling on a Posix system.");

This example uses the predefined versions Windows, OSX, and Posix. Swap the order of the Posix and OSX versions and the Posix block, not the OSX block, will run on Mac OS X. Remove the else statements and then both the Posix and OSX blocks will compile on Mac. Posix is defined on all POSIX systems, which includes Linux, Mac OS X, and the various BSDs. In addition to Windows and OSX, other system-specific versions include linux, FreeBSD, OpenBSD, NetBSD, DragonFlyBSD, BSD (for other flavors of BSD), Solaris, and more. You can find a list of predefined versions at http://dlang.org/version.html#predefined-versions.

Note

The fact that the linux version isn't capitalized is sort of an accident of history. linux is a predefined preprocessor symbol with the GCC compiler. Back in the D1 days, the same symbol was included in D under the assumption that Linux programmers would find it familiar. Over the years, some users have asked that it be deprecated and replaced with the capitalized form, but that hasn't happened as a great deal of D code already uses it. As such, it remains an anomaly among the predefined versions. It's also an occasional source of bugs. One such bug actually made it into Phobos, where some Linux-specific networking code was versioned with Linux instead of linux!

version does not create a new scope; anything declared inside a version block belongs to the enclosing scope. For example:

module timestuff;
// These imports are at global scope.
version(Windows) import core.sys.windows.windows;
else import core.sys.posix.time;

void doSomeTimeStuff() {
  // These variables are in function scope
  version(Windows) {
    SYSTEMTIME sysTime;
    // Do something with sysTime
  } else {
    timeval tv;
    // Do something with tv
  }
  int hour = sysTime.wHour;  // Compiles only on Windows!
}

New D programmers are often surprised that they are unable to use version with Boolean expressions, for example version(Windows || linux). Such a feature has been requested, but was rejected on the grounds that it leads to error-prone code. One way to handle this is to use a version specification. These can be declared in module scope, never in a local scope, and allow you to specify new versions in code.

version(Windows)
    version = WindowsOrLinux;
else version(linux)
    version = WindowsOrLinux;

Now version(WindowsOrLinux) can be used with one major caveat: version specifications only exist within the module in which they are declared. To use WindowsOrLinux in multiple modules, the preceding code must be included at the top of every module that needs it; it can't be implemented once and imported everywhere. Note that using a version before it is set is an error.

version(DoIt) pragma(msg, "DoIt!");
version = DoIt;     // Error

In addition to predefined operating system versions, there are versions for the currently recognized D compilers, CPU architectures, endianness, feature availability, and more. Additionally, version(unittest) is enabled only when -unittest has been passed to the compiler, version(assert) is satisfied only when asserts are enabled, and version(none) can be used to disable a block of code.

D allows custom versions to be specified on the command line with the –version compiler switch. For example:

dmd -version=SayHello foo.d

With this command line, the following snippet will print Hello, World!.

version(SayHello) writeln("Hello, World!");
else writeln("I have nothing to say.");

It's also possible to specify an integer version, which the compiler interprets as a version level. Any code in such a block will only be compiled when the number is greater than or equal to the number specified.

version(10) pragma(msg, "Ten is enabled!");

This form must use integer literals and can be specified either on the command line or with a version specification in code. To see the preceding message:

dmd -version=10 foo.d

Anything in a version block must be syntactically valid D. This can have unexpected consequences. It's possible when using different compilers, or even different versions of the same compiler, that code in a version block will not be syntactically valid, causing a compiler error. For this reason, some prefer to use version(none) to disable unused code rather than commenting it out; it helps ensure the code will not go stale.

The debug condition

Where version is intended to be used to facilitate porting across different platforms and configuring different program features, debug is intended to enable the inclusion of code used for debugging. It's only available when -debug is passed on the command line. Here's a simple example. Note that it uses no identifiers and no numeric debug level.

debug writeln("Debugging enabled.");
else writeln("Debugging disabled.");

Compile this with -debug and the first line will print; compile without that flag and the second line will print. If you need them, you can also use identifiers and numeric levels.

debug(Graphics)
  writeln("Graphics debugging enabled.");
debug(10)
  writeln("Debug level 10 enabled.");

Identifiers and levels can be specified in code using a debug specification, for example debug=10, where they do not create a new scope, or on the command line, for example -debug=Graphics. A debug specification is only valid for the module in which it is declared, but using the command-line flag enables it for the entire program. The meaning of a debug identifier or level is entirely up to you; that is, specifying –debug=Graphics does not enable any automatic debugging for a graphics package or module; any code you'd like enabled only in that case must be wrapped in debug(Graphics) blocks. -debug is shorthand for -debug=1 and, in source code, debug means debug(1).

The static if condition

The static if condition is a compile time version of the if statement. It can be used in any scope, including module scope, and can contain multiple else static if branches and a single, optional else at the end. When the condition of any branch is met, any code inside its block will be included in the final binary. No branch in a static if chain creates a new scope, so any variables declared inside will belong to the enclosing scope. As with other statements, if the block contains a single expression or statement, the braces can be omitted.

There are many uses for static if, but one that I've found particularly helpful is to create Boolean conditions for version combinations. Let's redo the version(WindowsOrLinux) example from earlier. Since conditions in a static if are evaluated at compile time, they need to be compile-time expressions. For that, we'll enlist the help of a couple of manifest constants.

version(Windows) {
  enum sysWindows = true;
  enum sysLinux = false;
}
else version(linux) {
  enum sysWindows = false;
  enum sysLinux = true;
}
else {
  enum sysWindows = false;
  enum sysLinux = false;
}

The preceding snippet is implemented once at module scope and that module is then imported anywhere these constants are needed. A static if statement with a logical OR condition is used to test if the platform is Windows or Linux.

void main() {
  static if(sysWindows || sysLinux)
    writeln("Windows or Linux!");
  else
    writeln("Neither Windows nor Linux!");
}

Any compile-time expression can be used as a static if condition. Don't forget that most of the built-in type properties are compile-time values. For example, the following configures a struct based on the host platform's architecture.

struct SomethingSilly {
  static if(size_t.sizeof == 8)       // 64-bit
    double value;
  else static if(size_t.sizeof == 4)  // 32-bit
    float value;
  else                               // Future proof
    pragma(msg, "Unsupported architecture.");
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.212.217