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
:
[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
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
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.