hsc2hsコマンドを使うと、CコードのHaskellバインディングを書く作業を一部自動化することができる。このコマンドは、特別な構文要素の埋め込まれたHaskellのコードを読み、Cのヘッダから得た情報を使ってこれらの要素を処理し、本当のHaskellファイルを出力する。この特別な構文要素は、HaskellからCのデータにアクセスするにあたって必要なことを扱う。
hsc2hsはCファイルとCヘッダを出力することもある。Cファイルには、プログラムにリンクすべき追加のC関数が置かれ、ヘッダファイルは、そのHaskellモジュールがコンパイルされてできるCコードからincludeされるものである。この二つのファイルは#defという構文要素(下記参照)が使われたときに作られる。
hsc2hsは実際には直接Haskellコードを出力するわけではない。問題のヘッダをインクルードするCプログラムを作り、自動的にコンパイル・実行する。このプログラムがHaskellコードを生成する。
以下では、「Haskellファイル」は主たる出力ファイル(通常.hs)であり、「Haskellファイルのコンパイル結果」は、ghcがHaskellファイルをコンパイルしてできたもの(.hcファイル)であり、「Cプログラム」はHaskellファイルを出力するプログラムであり、「Cファイル」は場合によって出力されるCファイルであり、「Cヘッダ」はそのヘッダファイルである。
hsc2hsは、引数として、入力ファイルと、振る舞いを変更するフラグを受け付ける。
-o FILEまたは--output=FILE
Haskellファイルの名前。
-t FILEまたは--template=FILE
テンプレートファイル(下記参照)。
-c PROGまたは--cc=PROG
利用するCコンパイラ(デフォルト: gcc)。
-l PROGまたは--ld=PROG
利用するリンカ(デフォルト: gcc)。
-C FLAGまたは--cflag=FLAG
Cコンパイラに渡す追加のフラグ。
-I DIR
Cコンパイラに渡される。
-L FLAGまたは--lflag=FLAG
リンカに渡す追加のフラグ。
-i FILEまたは--include=FILE
適切な#includeがソース中に置かれたのと同様。
-D NAME[=VALUE]または--define=NAME[=VALUE]
ソース中に適切な#defineが置かれたのと同様。
--no-compile
中間のCプログラムをディスクに書き込んだ後停止する。中間Cファイルの名前は、入力ファイル名の.hscを_hsc_make.cで置き換えたものである。
-k or --keep-files
通常通りに進行するが、中間ファイルを全く削除しない。
-x or --cross-compile
クロスコンパイルモードを起動する(12.2.4. クロスコンパイルを見よ)。
--cross-safe
.hscディレクティブを、--cross-compileモード(12.2.4. クロスコンパイルを見よ)が対応しているものに限定する。これは、安全にクロスコンパイルされねばならない.hscファイルがあって、クロスコンパイル不能な要素が忍び込むことがないようにしたい場合に便利なはずである。
-?または--help
利用可能なフラグの要約を表示し、成功裡に終了する。
-Vまたは--version
バージョン情報を表示し、成功裡に終了する。
入力ファイルは.hscで終わっていなければならない。(これは素のHaskellソースでなければならない。文芸Haskellは未対応である)。出力ファイルはデフォルトでは.hsc接尾辞を次のもので置き換えた名前になる。
.hs
|
Haskellファイル |
_hsc.h
|
Cヘッダ |
_hsc.c
|
Cファイル |
CプログラムはHaskellコンパイラでコンパイルされる。これにより、HsFFI.hが使える。これは、自動的にCプログラムにincludeされる。
特殊な処理は全て#演算子で起動される。#を文字どおり出力したい場合は、##と二回書けば良い。文字列リテラルおよびコメントの中の#は処理されない。
#の後にはスペースやタブ(省略可能)が続き、その後に、アルファベットと数字からなる、処理の種類を示すキーワードが書かれ、その後に引数が置かれる。引数は、Cの式をコンマで区切ったような形をしている(括弧では囲まない)。引数が終わるのは、対応するもののない)、]、}が現れるか、行の終わりに達したとき(ただし、() [] {} '' "" /**/のいずれにも囲まれておらず、バックスラッシュが前置されてもいない場合に限る)である。バックスラッシュと改行の対は消される。
さらに、#{何か}は#何かと同様だが、終わりが明確なので、行の最後に置いたり括弧の中に置いたりする必要がない。
個々のキーワードの意味は次の通り。
#include <file.h>,
#include "file.h"
指定されたファイルが、Cプログラム、Haskellプログラムのコンパイル結果、Cヘッダにそれぞれincludeされる。<HsFFI.h>は自動的にincludeされる。
#define 名前,
#define 名前 値,
#undef 名前
#includeに似ている。#includeと#defineは一つのファイルに二回置かれることがあることに注意。
#let 名前 引数列 = "定義"
Haskellソースに適用されるマクロを定義する。引数名はコンマで区切り、括弧では囲まない。このようなマクロは、#名前で始まる構文要素で起動される。この定義は、Cプログラムにおいて、括弧で囲んでprintfの引数として使われる。引数を参照するには、引用符を閉じ、引数を置き、再び引用符を開くことでCの文字列リテラルの連結を利用する。あるいはprintfの書式指定子を使っても良い。引数は、Cプリプロセッサの#引数記法を使って明示的に文字列化するのでない限り、文字列として渡されなければならない。
#def Cの定義
この定義(関数定義、変数定義、構造体定義、typedef)がCファイルに書かれ、そのプロトタイプかextern宣言がCヘッダに書かれる。インライン関数も正しく扱われる。構造体定義とtypedefはCプログラムにも書き込まれる。inline、struct、typedefの各キーワードはdefの直後に来なければならない。
#if 条件 ,
#ifdef 名前,
#ifndef 名前,
#elif 条件,
#else,
#endif,
#error メッセージ,
#warning メッセージ
条件コンパイルディレクティブは、Cプログラム、Cファイル、Cヘッダに変更なしで渡される。Cプログラムにも置かれるので、Haskellファイルの適当な部分が飛ばされることになる。
#const Cの式
式はlongまたはunsigned longに変換可能でなければならない。その値(リテラル、またはリテラルを符号反転したもの)が出力される。
#const_str Cの式
式はcharへの定数ポインタに変換可能でなければならない。その値(文字列リテラル)が出力される。
#type Cの型
Cの数値型に対応する、Haskellでの同等の型が出力される。これは、{Int,Word}{8,16,32,64}、Float、Double、LDoubleのいずれかである。
#peek 構造体型, フィールド
Cの構造体のフィールドを読み出す関数が出力される。型はStorable b => Ptr a -> IO bである。#peekと#pokeを使って、与えられたCの構造体についてのStorableクラスの演算を定義できるようにする、という意図である。(ライブラリ説明書のForeign.Storable参照)
#poke 構造体型, フィールド
pokeについても同様。型はStorable b => Ptr a -> b -> IO ()になる。
#ptr 構造体型, フィールド
構造体のフィールドへのポインタを作る。型はPtr a -> Ptr bになる。
#offset 構造体型, フィールド
構造体型中でのフィールドのオフセットをバイト単位で計算する。型はIntになる。
#size 構造体型
構造体型の大きさを、バイト単位で計算する。型はIntになる。
#enum 型, 構築子, 値, 値, ...
#constを使った複数の定義の代替となる略記法。それぞれの値はCの整定数(例えば列挙値)の名前である。この名前は、アンダースコアの次の文字を大文字にし、それ以外を小文字にし、アンダースコアを取り去ることで、Haskellの名前として使われる。値と書く代わりにHaskellでの名前 = Cの値と書くことで、自分で変換を指定することができる。この場合、Cの値は任意の式であって良い。Haskellでの名前は、指定された型を持つものとして定義される。定義は、指定された構築子(実際には式でも良いし、空でも良い)を、適当な整数値に適用したものである。一つの型に対して複数の#enum定義があっても良い。この構文要素は型定義自体を出力するわけではないからである。
#const、#type、#peek、#poke、#ptrはhsc2hsと結びつけられておらず、CプログラムにincludeされるCテンプレートであるtemplate-hsc.hで定義されている。自分で構文要素やテンプレートを用意し、それを使うこともできる。#要素で、キーワードが未知であるものは、全てCテンプレートが対応するものとみなされる。
Cテンプレートでは、名前にhsc_を前置したマクロまたは関数を定義する。この関数は、構文要素を処理し、展開結果を標準出力に書き出す。例としてtemplate-hsc.hを参照せよ。
このようなマクロはソース中で直接定義することもできる。これは、#letマクロを使ったものに展開される#let風マクロを作るのに便利である。通常の#letでは、マクロ名にhsc_を前置し、定義をprintfの呼び出しで包む。
hsc2hsは通常、Cプログラムを作成し、コンパイルし、実行することで動作する。しかし、この方策はクロスコンパイルでは機能しない。この場合、Cコンパイラはホスト機械ではなくターゲット機械で走るコードを生成するからである。この状況のために、hsc2hs --cross-compileという特殊なモードがある。これは、コンパイルのみ(細かく言うと、コンパイルの成否)によって情報を抽出して.hsを生成することができる。
--cross-compileは、.hsc構文の一部にしか対応していない。以下のものは未対応である。
#{const_str}
#{let}
#{def}