cond-expand
マクロしばしば、実装やプラットフォームがどういった機能を提供しているかに 応じてコードを切り替えたいことがあります。例えば、ネットワークが使えるか どうかで振る舞いを変えるとか、ほぼポータブルなコードの一部だけに 特定の実装に依存するコードを入れたいとかいう場合です。
C言語なら、#ifdef
のようなプリプロセッサディレクティブを、
Common Lispなら、#+
と#-
のようなリーダマクロを使うところです。
Schemeでは、こういう時にはcond-expand
を使います。
[R7RS base] プラットフォームがfeature-requirementに示される機能をサポートしていれば、 このマクロはcommand-or-definition … へと展開されます。
feature-requirement は以下のような構文でなければなりません。
feature-requirement : feature-identifier | (and feature-requirement …) | (or feature-requirement …) | (not feature-requirement) | (library library-name)
このマクロは feature-requirement を順にテストし、そのひとつが 満たされたら、対応する command-or-definition … に展開されます。
最後の節のfeature-requirementの部分にはシンボルelse
を
置くこともできます。他の節のfeature-requirementが満たされなかった場合に
その節が展開されます。
条件を満たす節もelse
節も無かった場合、cond-expand
は
エラーを通知します。この仕様は、
必要な機能を欠いている実行環境を早めに検出するためです。
もしチェックする機能がオプショナル、つまりそれが無くてもプログラムの実行自体は
可能であるというものであるなら、最後に空のelse
節を忘れないようにしてください。
(cond-expand [feature expr] ; some optional feature [else])
feature-identifier は機能を示すシンボルです。その機能が現在のプラットフォームで サポートされているなら、それは、feature-requirement を満たします。 より複雑な条件を構成するために、feature-requirement のブール代数による 組み合わせを用いることが出来ます。
(library library-name)
という形式はR7RSで追加されたもので、
指定されるライブラリが使える場合に条件が満たされます。
library-nameは、R7RS形式
(例: (srfi 13)
) も伝統的なGaucheのモジュール名
(例: srfi.13
) も使えます。
例えば、Gauche、Chicken Scheme、ChibiSchemeに依存するコードを 入れたいとしましょう。現代の多くのScheme実装は、自分自身を示す feature-identifierを定義しているので、条件付きのコードは次のとおり書けます。
(cond-expand [gauche (gauche-specific-code)] [(or chicken chibi) (chicken-chibi-specific-code)] [else (fallback-code)] )
cond-expand
の条件はマクロ展開時に全て処理され、
不採用だった節のコードは捨てられる、という事実は重要です。
このおかげで、例えば実装によって認識されないマクロ呼び出しや言語拡張を
command-or-definitionに含めることができます。
また、グローバルな束縛を条件によって定義したりしなかったりすることができます。
これをcond
と比較してみましょう。cond
は条件を実行時に調べます。
もし条件節の中に、あるプラットフォームではサポートされないマクロ呼び出しを
書いた場合、たとえその節が現実には決して実行されないものであるとしても、
マクロ展開時にエラーになってしまうかもしれません。
また、cond
を使って条件的にグローバルな束縛を定義することはできません。
ただし、マクロ展開した結果をどうにかして保存しておいて、 別のプラットフォームでそれを走らせる、ということをする場合は注意が必要です。 条件選択はマクロ展開をするプラットフォームの機能に応じて行われ、 それはコードを実行するプラットフォームの機能とは一致しないかもしれません (これはもちろん、クロスコンパイルで常に問題となることです。)
註: cond-expand
がSRFI-0として定義された時には、
Schemeライブラリの命名法の標準が決まっていませんでした。
SRFI-0のドキュメントではsrfi-N
という形式の機能識別子で特定のsrfiが
サポートされているかどうかを検査するように推奨されていました。
この使い方は現在は非推奨です。(library srfi.N)
で検査するように
変更してください。詳しくはSRFIが使えるかどうかテストする参照。
0.9.13時点では、環境変数GAUCHE_WARN_SRFI_FEATURE_ID
が
セットされている場合に限り、機能識別子srfi-N
が使われた時に
警告が出されます。将来はデフォルトで警告を出すようにするので、
いまのうちにこの環境変数をセットして古いコードを見つけて書き換えておくと良いでしょう。
Gaucheで使えるfeature identifier一覧は下を見てください。
gauche
gauche-X.X.X
プログラムをGaucheで実行していることを示します。ポータブルなプログラムの中に
Gauche特有のコードを埋め込むのに便利です。
X.X.X
はGaucheのバージョンで(例: gauche-0.9.4
)、
特定のGaucheのバージョンのみに依存したコードを入れるのに使えます。
(処理系のバージョンつきの機能識別子はR7RSで示唆されているものですが、
バージョンを比較する機能がないとあまり有用ではないでしょう。
将来はそのような機能が追加されるかもしれません。)
gauche.os.windows
gauche.os.cygwin
それぞれ、WindowネイティブプラットフォームとCygwin/Windowsプラットフォームを 示します。どちらも定義されていなければ、Unix系と考えて構いません。 (CygwinもUnix系と言えなくはありませんが、他のUnix系に比べて 色々違いがあるので、機能識別子を用意してあります。)
gauche.ces.utf8
Gaucheの内部エンコーディングがutf-8
であることを示します。
1.0以前には、Gaucheはutf-8以外のいくつかの内部エンコーディングで
コンパイルすることが可能で、それに合わせた機能識別子が定義され、
プログラムが内部エンコーディングに応じて振る舞いを変えられるようになっていました。
今はこれが唯一のエンコーディングです。過去のコードを変更なしで走らせられるように
定義されています。
gauche.net.tls
gauche.net.tls.axtls
gauche.net.tls.mbedtls
TLS/SSLサポートがあれば定義されます。サブ機能識別子gauche.net.tls.axtls
と
gauche.net.tls.mbedtls
は、それぞれサブシステムaxTLS
とmbedTLS
がサポートされていれば定義されます。
gauche.net.ipv6
IPv6サポートが組み込まれていれば定義されます。 ただし、これがGaucheがIPv6サポート込みでビルドされたことを示すだけです。 OSがIPv6サポートをオフにしている場合、IPv6機能を使おうとするとシステムエラーが 投げられます。
gauche.sys.threads
gauche.sys.pthreads
gauche.sys.wthreads
マルチスレッドがサポートされていればgauche.sys.threads
が定義されます
(gauche.threads
- スレッド参照)。また、マルチスレッドは内部ではプラットフォームによって
POSIXスレッドかWindowsスレッドを利用しますが、前者の場合は
gauche.sys.pthreads
が、後者では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
これらシステムの機能が使えるのなら対応する機能識別子が定義されます。
r7rs
実装がr7rs準拠であることを示します。
exact-closed
数値計算が正確な値について閉じている、つまり、正確な数を ゼロ以外の正確な数で割った結果が常に正確な数になることを示します。
ieee-float
内部的にIEEE浮動小数点数を使っていることを示します。
full-unicode
Unicodeの全ての範囲をサポートしていることを示します。
ratios
有理数のサポートがあることを示します。
posix
windows
プラットフォームによってどちらかが定義されます。
big-endian
little-endian
プラットフォームによってどちらかが定義されます。