For Development HEAD DRAFTSearch (procedure/syntax/module):

4.12 Feature conditional

The cond-expand macro

Sometimes you need to have a different piece of code depending on available features provided by the implementation and/or platform. For example, you may want to switch behavior depending on whether networking is available, or to embed an implementation specific procedures in otherwise-portable code.

In C, you use preprocessor directives such as #ifdef. In Common Lisp, you use reader macro #+ and #-. In Scheme, you have cond-expand:

Macro: cond-expand (feature-requirement command-or-definition …) …

[R7RS base] This macro expands to command-or-definition … if feature-requirement is supported by the current platform.

feature-requirement must be in the following syntax:

feature-requirement
  : feature-identifier
  | (and feature-requirement ...)
  | (or  feature-requirement ...)
  | (not feature-requirement)
  | (library library-name)

The macro tests each feature-requirement in order, and if one is satisfied, the macro itself expands to the corresponding command-or-definition ….

The last clause may have else in the position of feature-requirement, to make the clause expanded if none of the previous feature requirement is fulfilled.

If there’s neither a satisfying clause nor else clause, cond-expand form throws an error. It is to detect the case early that the platform doesn’t have required features. If the feature you’re testing is optional, that is, your program works without the feature as well, add empty else clause as follows.

(cond-expand
  [feature expr]  ; some optional feature
  [else])

feature-identifier is a symbol that indicates a feature. If such a feature is supported in the current platform, it satisfies the feature-requirement. You can do boolean combination of feature-requirements to compose more complex conditions.

The form (library library-name) is added in R7RS, and it is fulfilled when the named library is available. For the library-name, you can use both R7RS library name (e.g. (srfi 13)) and traditional Gauche module name (e.g. srfi.13).

Here’s a typical example: Suppose you want to have implementation-specific part for Gauche, Chicken Scheme and ChibiScheme. Most modern Scheme implementations defines a feature-identifier to identify itself. You can write the conditional part as follows:

(cond-expand
 [gauche  (gauche-specific-code)]
 [(or chicken chibi) (chicken-chibi-specific-code)]
 [else    (fallback-code)]
 )

It is important that the conditions of cond-expand is purely examined at the macro-expansion time, and unfulfilled clauses are discarded. Thus, for example, you can include macro calls or language extensions that may not be recognized on some implementations. You can also conditionally define global bindings.

Compare that to cond, which examines conditions at runtime. If you include unsupported macro call in one of the conditions, it may raise an error at macro expansion time, even if that clause will never be executed on the platform. Also, it is not possible to conditionally define global bindings using cond.

There’s a caveat, though. Suppose you want to save the result of macro expansion, and run the expanded result later on other platforms. The result code is based on the features of the platform the macro expansion takes place, which may not agree with the features of the platform the code will run. (This issue always arises in cross-compiling situation in general.)

NB: When cond-expand was defined as SRFI-0, there was no standard way to name Scheme libraries. So SRFI-0 suggested to use srfi-N as feature identifier to check the support of a specific srfi. We deprecate this usage. It should be replaced with (library srfi.N). See Testing availability of SRFIs, for the details.

As of 0.9.13, a warning is issued when srfi-N feature identifier is used in cond-expand if the environment variable GAUCHE_WARN_SRFI_FEATURE_ID is set. In future versions the warning will be default; we recommend to set this environment variable to detect code to update.

See below for the list of feature identifiers defined in Gauche.

Gauche-specific feature identifiers

gauche
gauche-X.X.X

Indicates you’re running on Gauche. It is useful to put Gauche-specific code in a portable program. X.X.X is the gauche’s version (e.g. gauche-0.9.4), in case you want to have code for specific Gauche version. (Such feature identifier is suggested by R7RS; but it might not be useful if we don’t have means to compare versions. Something to consider in future versions.)

gauche.os.windows
gauche.os.cygwin

Defined on Windows-native platform and Cygwin/Windows platform, respectively. If neither is defined you can assume it’s a unix variant. (Cygwin is supposedly unix variant, but corners are different enough to deserve it’s own feature identifier.)

gauche.ces.utf8

This indicates Gauche’s internal encoding is utf-8. Before 1.0, Gauche can be compiled with internal encodings other than utf-8, and the corresponding feature identifiers are defined so that the program can switch its behavior according to it. Now this is the only possible choice. This is kept for the backward compatibility.

gauche.net.tls
gauche.net.tls.axtls
gauche.net.tls.mbedtls

Defined if the runtime supports TLS in networking. The two sub feature identifiers, gauche.net.tls.axtls and gauche.net.tls.mbedtls, are defined if each subsystem axTLS and mbedTLS is supported, respectively.

gauche.net.ipv6

Defined if the runtime supports IPv6. Note that this only indicates Gauche has been built with IPv6 support; the OS may not allow IPv6 features, in that case you’ll get system error when you try to use IPv6.

gauche.sys.threads
gauche.sys.pthreads
gauche.sys.wthreads

If the runtime supports multithreading, gauche.sys.threads is defined (see gauche.threads - Threads). Multithreading is based on either POSIX pthreads or Windows threads. The former defines gauche.sys.pthreads, and the latter defines gauche.sys.wthreads.

gauche.sys.sigwait
gauche.sys.setenv
gauche.sys.unsetenv
gauche.sys.clearenv
gauche.sys.getloadavg
gauche.sys.getrlimit
gauche.sys.lchown
gauche.sys.getpgid
gauche.sys.nanosleep
gauche.sys.crypt
gauche.sys.symlink
gauche.sys.readlink
gauche.sys.select
gauche.sys.fcntl
gauche.sys.syslog
gauche.sys.setlogmask
gauche.sys.openpty
gauche.sys.forkpty

Those are defined based on the availability of these system features of the platform.

R7RS feature identifiers

r7rs

Indicates the implementation complies r7rs.

exact-closed

Exact arithmetic operations are closed; that is, dividing an exact number by a non-zero exact number always yields an exact number.

ieee-float

Using IEEE floating-point number internally.

full-unicode

Full unicode support.

ratios

Rational number support

posix
windows

Either one is defined, according to the platform.

big-endian
little-endian

Either one is defined, according to the platform.



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT