指针的编组向量

Marshaling vectors of pointers

我试图将任意右嵌套对(即 Vector (Int64, (Int64, (...))))的 Haskell vector 表示为 C 中的二维数组(即 int64_t**), 首先索引为向量分量,然后是元组分量。

这是我的 C 函数:

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");
    }
}

在Haskell这边我有:

{-# 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)

这会打印

Moduli: 10,11,
vec
Segmentation fault

这让我认为我的 Storable (a,b) 实例没问题:我得到一个指向 (Int64,Int64) 的指针,然后将其转换为 Ptr Int64,然后读取数据就好了在C中。所以问题是向量出了什么问题?我正在尝试做同样的事情:创建一个 Vector (Int64, Int64),为其获取一个 Ptr (Int64, Int64) 类型的指针,并将其转换为 Ptr (Ptr Int64)。当我尝试访问 C 中的数组时,为什么会出现段错误?封送此数据的正确方法是什么?

您没有在两侧使用相同的数据格式。您的 Haskell 代码生成一个平面数组,其中包含向量的所有元组中的所有值,而您的 C 代码需要一个 指针数组 ,每个元素对应向量的一个元素,指向该元组中的值。

如果你能像这样声明你的 C 函数(也许这是现在有效的 C,我不知道)

void test(int64_t (*y)[tupSize], int32_t vecSize, int16_t tupSize, int64_t *tup)

那么 C 将使用与 Haskell 相同的布局。否则,您可以使用

手动索引
//  SINGLE pointer |
//                 v
void test(int64_t *y, int32_t vecSize, int16_t tupSize, int64_t *tup)
...
    printf("%" PRId64 ",", y[i*tupSize+j]);