hsc2hsコマンドを使うと、CコードのHaskellバインディングを書く作業を一部自動化することができる。このコマンドは、特別な構文要素の埋め込まれたHaskellのコードを読み、Cのヘッダから得た情報を使ってこれらの要素を処理し、本当のHaskellファイルを出力する。この特別な構文要素は、CのデータをHaskellからアクセスするにあたって必要なことを扱う。
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
で置き換えたものである。
-?
または––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
の呼び出しで包む。