2011-12-21 50 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