以前、Win32 APIのMultiByteToWideChar、WideCharToMultiByteを使って UTF-16 ⇔ Shift-JIS 変換するコードを書きましたが、コードの一部にCで記述した部分があります。本当はCを全く使わないでHaskellで書きたかったのですが問題があって出来ませんでした。
何故かというといわゆる「ポインタ渡し」が出来なかったのです。
Cで関数を呼ぶ場合、関数とデータのやり取りをするのに、まず、領域を確保し、関数にそのアドレスを渡します。関数はそのアドレスが示すデータを書き変えることによりデータを渡します。この方法を行いたいのです。
Haskellの変数は値を定義するだけで、渡された引数に値を書き込むことができません。
ポインタを使ったデータの書き込み、読み取りをHaskellで実行する方法について調べてみました。
> :m Foreign.Storable Foreign.Marshal.Alloc -- メモリを確保するための関数 malloc の型を表示させてみます。 > :t malloc malloc :: Storable a => IO (GHC.Ptr.Ptr a) -- 引数がないのですけれど、そのままmallocを実行するとエラーになります。 > malloc <interactive>:78:1: Ambiguous type variable `a0' in the constraint: (Storable a0) arising from a use of `malloc' Probable fix: add a type signature that fixes these type variable(s) In the expression: malloc In an equation for `it': it = malloc -- add a type signature that fixes these type variable(s) と言って -- います。 -- Haskell得意の「出力する型を指定すればその型に応じた値を返す」って -- やつですね。 > malloc::IO (GHC.Ptr.Ptr Int) -- >0x04192e10 > n <- malloc::IO (GHC.Ptr.Ptr Int) --> Int型のメモリを確保します。 -- poke する前にpokeの型を確認 > :t poke -- > poke :: Storable a => GHC.Ptr.Ptr a -> a -> IO () -- malloc で確保した領域をpokeに渡したときに返す関数の型を確認すると -- Intを引数とすることが分かります。 > :t poke n poke n :: Int -> IO () -- 7 を書き込んでみます。 > poke n 7 > :t peek n peek n :: IO Int -- 書き込んだ値を読んでみます。 > peek n -- > 7 -- 確保した領域を解放した後はゴミが読みだされました。 > free n > peek n 31183736 -- 10バイトメモリを確保します。 > m <- mallocBytes 10:: IO (GHC.Ptr.Ptr Int8) -- 書き込みます > pokeByteOff m 0 (1::Int8) > pokeByteOff m 1 (2::Int8) > pokeByteOff m 2 (3::Int8) -- 読み出します > peekByteOff m 0 -- > 2.535e-321 -- あれ?? -- IO a の a は型変数なので型を指定していすればよさそうです。 > :t (peekByteOff m 0) -- > peekByteOff m 0 :: Storable a => IO a > :t peekByteOff m 0::IO Int8 -- > peekByteOff m 0::IO Int8 :: IO Int8 > peekByteOff m 0::IO Int8 -- > 1 > peekByteOff m 1::IO Int8 -- > 2 > peekByteOff m 2::IO Int8 -- > 3
参考