定义可存储的涉及向量的递归数据结构

时间:2011-12-21 19:11:39

标签: haskell vector storable

我有以下表格的数据结构(V是Data.Storable.Vector):

data Elems = I {-# UNPACK #-} !GHC.Int.Int32
             | S {-# UNPACK #-} !GHC.Int.Int32 {-# UNPACK #-} !(Ptr CChar)
             | T {-# UNPACK #-} !(V.Vector Elems)
                deriving (Show)

我首先为非递归形式编写了一个自定义可存储定义(即没有T构造函数)。然后,我尝试使用T中的ForeignPtrlength信息为Vector添加自定义查看和戳定义(代码如下)。 GHC编译器抱怨Storable实例没有为ForeignPtr Elems类型定义。我的问题是,是否可以将ptr存储到可存储定义中的Vector,而不必强制为ForeignPtr编写可存储的实例定义。

Haddocs文档中,ForeignPtr似乎只是一个分配了Finalizer的Ptr:

  

ForeignPtrs和香草记忆之间的本质区别   Ptr a类型的引用是前者可能与之相关联的   终结。

由于最终确定问题,我不想使用Ptr代替ForeignPtr来解决此问题。因此,我更喜欢存储ForeignPtr的位置(通过Ptr (ForeignPtr a)),以便GHC垃圾收集器知道对它的引用。但是,这种方法会迫使我定义Storable instance(因为约束(Storable a) => Ptr a这是有道理的)。

有没有办法将Ptr存储和检索到Storable中的Vector,而无需为ForeignPtr定义可存储的实例?如果没有,那么编写ForeignPtr的可存储定义是必须的。在那种情况下,它会是什么样子?我的猜测是它只会将一个Ptr存储到ForeignPtr。

以下完整代码:

{-# LANGUAGE MagicHash #-}
import qualified Data.Vector.Storable as V
import Foreign
import Foreign.C.Types (CChar)
import Foreign.Marshal.Array (lengthArray0)
import GHC.Int

data Elems = I {-# UNPACK #-} !GHC.Int.Int32
             | S {-# UNPACK #-} !GHC.Int.Int32 {-# UNPACK #-} !(Ptr CChar)
             | T {-# UNPACK #-} !(V.Vector Elems)
                deriving (Show)

instance Storable Elems where
  sizeOf _ = sizeOf (undefined :: Word8) + sizeOf (undefined :: Int32) + sizeOf (undefined :: Ptr CChar)
  alignment _ = 4

  {-# INLINE peek #-}
  peek p = do
      let p1 = (castPtr p::Ptr Word8) `plusPtr` 1 -- get pointer to start of the element. First byte is type of element
      t <- peek (castPtr p::Ptr Word8)
      case t of
        1 -> do 
          x <- peek (castPtr p1 :: Ptr GHC.Int.Int32) 
          return (I x)
        2 -> do 
          x <- peek (castPtr p1 :: Ptr GHC.Int.Int32) 
          y <- peek (castPtr (p1 `plusPtr` 4) :: Ptr (Ptr CChar)) -- increment pointer by 4 bytes first
          return (S x y)
        _ -> do
          x <- peek (castPtr p1 :: Ptr Int)
          y <- peek (castPtr (p1 `plusPtr` 8) :: Ptr (ForeignPtr Elems)) 
          return (T (V.unsafeFromForeignPtr y 0 x)) -- return vector

  {-# INLINE poke #-}
  poke p x = case x of
      I a -> do
        poke (castPtr p :: Ptr Word8) 1  
        poke (castPtr p1) a
      S a b -> do
        poke (castPtr p :: Ptr Word8) 2
        poke (castPtr p1) a
        poke (castPtr (p1 `plusPtr` 4)) b -- increment pointer by 4 bytes first
      T x -> do
        poke (castPtr p :: Ptr Word8) 3
        let (fp,_,n) = V.unsafeToForeignPtr x
        poke (castPtr p1) n
        poke (castPtr (p1 `plusPtr` 8)) fp

      where  p1 = (castPtr p :: Ptr Word8) `plusPtr` 1 -- get pointer to start of the element. First byte is type of element

1 个答案:

答案 0 :(得分:3)

ForeignPtr无法成为Storable,因为它们的实现需要一种方法将一个或多个终结器指针与原始指针相关联,并且此关联与运行时相关。要使ForeignPtr可存储,您需要存储关联的Ptr(这很容易)和关联的终结器数组(这是不可能的,因为终结器是运行时内部的,并且可能绑定到GHC运行时的GC。)

这不是这里需要解决的问题。

问题在于,没有合理的方法可以将包含Vector的内容添加到Storable内。 Vector要求托管内存的内容(Storable.Vector的定义为data Vector a = Vector Int (ForeignPtr a)加上一些严格注释),但Storable的全部目的是将一些值存储到非托管内存中。此外,Vector根据其长度使用不同数量的内存,但Storable数据结构必须使用常量内存量。

您需要重新考虑数据结构尝试建模的内容。你真的需要存储这样的Vector吗?请注意,您要存储Vector Elems,这意味着您的值T包含Vector,其中包含T,其中包含{ {1}}包含Vector

我认为您可能会尝试对以下数据结构进行建模,但我可能错了:

T

如果您确实需要所描述的递归数据结构,请尝试实现此目的:

data Elems = OneElem Elem | ManyElems (Vector Elem)

data Elem
    = I !GHC.Int.Int32
    | S !GHC.Int.Int32 !(Ptr CChar)

指向某些data Elems = I !GHC.Int.Int32 | S !GHC.Int.Int32 !(Ptr CChar) | T !GHC.Int.Int32 !(Ptr Elems) 的指针使用常量内存,并且可以指向非托管内存,因此您可以为其创建可存储的实例。