我有下面的形式的一個數據結構(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
構造函數)。然後,我試圖使用ForeignPtr
和length
信息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
您可能需要考慮使用'-funbox-strict-fields'而不是在每個字段上放置'{ - #UNPACK# - }'編譯指示。 – ehird 2011-12-21 19:15:29