ベクトルのリストを渡す必要がある ac ライブラリの単純なラッパーを作成しています。配列へのポインターの配列を取ります。素敵なインターフェイスを作成するには、Vector (または Vector のリスト) が必要ですが、慣用的な Haskell でこれを行う方法を実際に見つけることはできません。(または、メモコピー以外の方法)。
私が探しているのは次のようなものです
Vector (Vector Foo) -> (Ptr (Ptr Foo) -> IO a) -> IO a
C 関数に渡すので、 を使用する必要がありますData.Vector.Storable
。ベクトルのベクトルを渡すことはできませんStorable
。これは、単に配列へのポインタのベクトルではなく、サイズとオフセット情報も含まれているためです。
たとえば、C 関数の引数が の場合int myCFunc(foo** arrays, int sz)
、次のコードが機能するはずです。
import Data.Vector.Storable
import Foreign.Storable
import Foreign.ForeignPtr
withCFunction :: Storable a => -- ^ Storable so compatible with C
Vector (Vector a) -- ^ vector of vectors
-> (Ptr (Ptr a) -> IO b) -- ^ C function wrapped by FFI
-> IO b
withCFunction v f = do
vs <- mapVectorM (\x -> let (fp,_,_) = unsafeToForeignPtr x
in unsafeForeignPtrToPtr fp) v
mapVectorM_ (\x -> let (tfp,_,_) = unsafeToForeignPtr x
in touchForeignPtr tfp) vs
let (vfp,_,_) = unsafeToForeignPtr vs
withForeignPtr vfp $ \p -> f p
編集: hCsound はこの正確なケースを扱っていないため、以下に完全な例を追加しました。
私のパッケージhCsound ( darcs repo )を見たいかもしれませんが、これは非常によく似たケースに対処しなければなりません。
C ライブラリがa によって使用される配列を変更しないことが非常に重要であることに注意してくださいData.Vector.Storable.Vector
。データを変更する必要がある場合は、最初に古いデータをコピーし、ffi を使用して配列を変更し、最後にポインターを新しいベクターにラップする必要があります。
これがコードです。コメントで指摘されたようにData.Vector.Storable.Vector
、 Storable インスタンス自体がないため、外側のベクトルをData.Vector.Vector
.
import Foreign.Storable
import Foreign.Ptr
import Foreign.ForeignPtr
import Foreign.Marshal.Array
import qualified Data.Vector as V
import qualified Data.Vector.Storable as S
import Data.Vector.Storable.Internal
withPtrArray v f = do
let vs = V.map S.unsafeToForeignPtr v -- (ForeignPtr, Offset, Length)
ptrV = V.toList $ V.map (\(fp,off,_) -> offsetToPtr fp off) vs
res <- withArray ptrV f
V.mapM_ (\(fp,_,_) -> touchForeignPtr fp) vs
return res
配列はによって割り当てられることwithArray
に注意してください。したがって、関数が戻った後、自動的に gc されます。
これらの配列は null で終了しないため、長さが他の方法で C 関数に渡されていることを確認する必要があります。
withForeignPtr
使用されません。代わりにtouchForeignPtr
、C 関数が終了する前に ForeignPtr の割り当てが解除されないようにするために呼び出されます。を使用するにはwithForeignPtr
、内部ベクトルごとに呼び出しをネストする必要があります。それnest
が、hCsound コードの関数が行うことです。を呼び出すだけではなく、かなり複雑ですtouchForeignPtr
。