I am making a simple wrapper for a c library that needs to have a list of vectors passed to it. It takes an array of pointers to arrays. To make a nice interface I'd like to have Vector (or list) of Vectors, but I can't really find out how to do this in idiomatic haskell. (Or any other way than memcopying stuff around).
What I'm looking for is something like
Vector (Vector Foo) -> (Ptr (Ptr Foo) -> IO a) -> IO a
Since you're passing to a C function, you should use Data.Vector.Storable. You can't just pass a vector of vectors of Storable because that will not be a vector merely of pointers to arrays, it also includes size and offset information.
if the argument to the C function is, say, int myCFunc(foo** arrays, int sz) then the following code should work:
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
Edit: hCsound doesn't deal with this exact case, so I've added a full example below.
You might want to look at my package hCsound (darcs repo), which has to deal with a very similar case.
Note that it's very important that the C library doesn't modify the arrays used by a Data.Vector.Storable.Vector. If you do need to modify the data, you should copy the old data first, modify the array through the ffi, and finally wrap the pointers into a new Vector.
Here's the code. As was pointed out in a comment, Data.Vector.Storable.Vector doesn't have a Storable instance itself, so you'll need the outer vector to be a 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
Note the array is allocated by withArray, so it's automatically gc'd after the function returns.
These arrays aren't null-terminated, so you'll need to make sure that the length is passed to the C function by some other method.
withForeignPtr isn't used. Instead, touchForeignPtr is called to ensure that the ForeignPtr's aren't deallocated before the C function is finished. In order to use withForeignPtr, you'd need to nest calls for each internal vector. That's what the nest function in the hCsound code does. It's rather more complicated than just calling touchForeignPtr.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With