2011-12-21 51 views
2

我有下面的形式的一个数据结构(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构造函数)。然后,我试图使用ForeignPtrlength信息Vector(代码如下)为T添加自定义窥视和戳定义。 GHC编译器抱怨Storable实例未被定义为ForeignPtr Elems类型。我的问题是,如果可以将ptr存储到Vector中的Stable定义中,而不必强制写入ForeignPtr的Storable实例定义。

Haddocs文档,ForeignPtr似乎只是分配给它的终结一个PTR:

ForeignPtrs和香草内存类型PTR上 引用的本质区别在于,前者可伴 终结者。

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

有没有一种方法来存储和检索ptr到存储的Vector中,而没有定义ForeignPtr的存储实例?如果没有,那么编写ForeignPtr的Storable定义是必须的。在那种情况下,它会是什么样子?我的猜测是它只会将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

您可能需要考虑使用'-funbox-strict-fields'而不是在每个字段上放置'{ - #UNPACK# - }'编译指示。 – ehird 2011-12-21 19:15:29

回答

3

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

虽然这不是需要解决的问题。

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

您需要重新考虑您的数据结构试图建模的内容。你真的需要存储这样的Vector吗?请记住,你是存储的ElemsVector,这意味着你可以有一个包含Vector包含T包含Vector包含TT

我认为你可能会尝试建模下面的数据结构,而不是,但我可能是错的:

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) 

一个指向某个Elems使用不断内存,并且可以指向非托管内存,所以你可以为它创建可存储的实例。

+0

感谢您的详细发布。我试图对递归向量建模 - 其原因是因为混合向量(例如Vector(字符向量,Ints向量,Vector of Floats,Vector))我必须在交互时处理与真实世界中的数据。我简化了我的例子。它会更像:data Elems = I!Int32 | IV!(Vector Int32)| ... | T!(矢量元素)。如果我没有弄错,你对Elems的第二个定义仍然无法处理。但是,我从你的第一个定义中得到了一些想法。现在处理它们 – Sal 2011-12-22 00:20:43