Reputation: 21811
I'm trying to represent a Haskell vector of arbitrarily right-nested pairs (i.e. Vector (Int64, (Int64, (...)))
) as a 2-d array in C (i.e. int64_t**
), indexed first as the vector component, then the tuple component.
Here's my C function:
void test(int64_t** y, int32_t vecSize int16_t tupSize, int64_t* tup)
{
printf("Tup vals: ");
for(int i = 0; i < tupSize; i++) {
printf("%" PRId64 ",",tup[i]);
}
printf("\n");
printf("vec\n");
for(int i = 0; i < vecSize; i++) {
printf("%d: (", i);
for(int j = 0; j < tupSize; j++) {
printf("%" PRId64 ",", y[i][j]);
}
printf(")\n");
}
}
On the Haskell side I have:
{-# LANGUAGE ScopedTypeVariables #-}
import Data.Int
import Data.Vector.Storable (Vector, singleton, unsafeWith)
import Foreign.Marshal.Utils (with)
import Foreign.Ptr
import Foreign.Storable (Storable (..))
foreign import ccall unsafe "test" test :: Ptr (Ptr Int64) -> Int64 -> Int16 -> Ptr Int64 -> IO ()
-- instance assumes right-nested pairs, but not enforced at type level
instance (Storable a, Storable b)
=> Storable (a,b) where
sizeOf _ = (sizeOf (undefined :: a)) + (sizeOf (undefined :: b))
alignment _ = max (alignment (undefined :: a)) (alignment (undefined :: b))
peek p = do
a <- peek (castPtr p :: Ptr a)
b <- peek (castPtr (plusPtr p (sizeOf a)) :: Ptr b)
return (a,b)
poke p (a,b) = do
poke (castPtr p :: Ptr a) a
poke (castPtr (plusPtr p (sizeOf a)) :: Ptr b) b
main :: IO ()
main = do
let tup = (10,11) :: (Int64, Int64)
vec = singleton (2,3) :: Vector (Int64, Int64)
with tup $ \tptr ->
unsafeWith vec $ \vptr ->
test (castPtr vptr) 1 2 (castPtr tptr)
This prints
Moduli: 10,11,
vec
Segmentation fault
which leads me to think that my Storable (a,b)
instance is fine: I'm getting a pointer for (Int64,Int64)
, then casting it to Ptr Int64
, and reading the data just fine in C. So the question is what is going wrong with the vector? I'm attempting to do the same thing: create a Vector (Int64, Int64)
, get a pointer of type Ptr (Int64, Int64)
for it, and cast it to a Ptr (Ptr Int64)
. Why am I getting a segfault when I try to access the array in C, and what is the proper way to marshal this data?
Upvotes: 5
Views: 176
Reputation: 14999
You're not using the same data format on the two sides. Your Haskell code produces a flat array of all the values in all the tuples of the vector, while your C code expects an array of pointers, one for each element of the vector, pointing to the values in that tuple.
If you could declare your C function like this (maybe this is valid C nowadays, I don't know)
void test(int64_t (*y)[tupSize], int32_t vecSize, int16_t tupSize, int64_t *tup)
then C would be using the same layout as Haskell. Otherwise, you can index manually with
// SINGLE pointer |
// v
void test(int64_t *y, int32_t vecSize, int16_t tupSize, int64_t *tup)
...
printf("%" PRId64 ",", y[i*tupSize+j]);
Upvotes: 4