Determining whether a module is available

We might want to compile some extension features if and only if another module is available at compile-time to gracefully degrade when a dependency isn't installed. The most straightforward solution is to use the version statement, passing the appropriate identifier to the dmd command line. Can we also do it without extra compiler options from the user?

How to do it…

Let's execute the following steps to determine whether a module is available:

  1. Try to compile an anonymous function that does nothing other than import a module.
  2. Use it conditionally by wrapping any use of the module inside the static if statement.
  3. Instruct the user to enable the module by placing it at a location where the compiler can find it automatically, or by adding the following module to the compile command line.

The code is as follows:

static if(__traits(compiles, { import test.foo; })) {
    import test.foo; /* safe to use */
} else {
   // module not available, work around
}

How it works…

This is an example of a common strategy to poke around D: conditional compilation. Instead of trying to find metadata, we can often simply try to compile a block of code to attempt the task we're interested in. If that compilation fails, assume the task cannot be done with the arguments we're given. Anything we can encapsulate in a small function can be tested with __traits(compiles).

Note

This principle is also how the duck-typed interface checking for ranges works, as discussed in Chapter 3, Ranges, and Chapter 7, Correctness Checking.

Since imports can be local to a function, we can wrap it in an anonymous function (the { code } syntax expands to void delegate() { code }) and see if it compiles. Once it does, the compiler has already loaded the module, so we can immediately use it.

Generally, any time you might get a compilation error and do not wish for it to be fatal, you may wrap the code in static if(__traits(compiles)) to selectively enable or disable iffy features. However, be careful not to overuse this. It is easy to hide minor mistakes and completely disable the code that never actually compiles! Keep the conditional code as simple as possible and always run it normally when you change it. This ensures that it actually does work when it is supposed to.

The __traits function is a built-in language construct for special communication with the compiler. It always takes an operation identifier as the first argument, for example: compiles, isSame, allMembers, getMember, and others that can be found in the documentation. Subsequent arguments are specific to the operation. The compiles identifier takes only one argument; an expression that it attempts to compile.

The __traits function returns values in the form of compile-time constants that can be tested in static if, stored in enums or aliases, or used directly. In the generated code, they will be indistinguishable from data literals. When __traits returns a list, it does it as a TemplateArgumentList (sometimes also called a TypeTuple, though it can hold more things other than types). These lists may be looped over with the foreach loop at compile time to inspect individual elements with all compile-time tools.

See also

..................Content has been hidden....................

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