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