Определение хранимых для рекурсивной структуры данных с участием векторов - PullRequest
2 голосов
/ 21 декабря 2011

У меня есть структура данных в форме ниже (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). Затем я попытался добавить пользовательское определение peek и poke для T, используя ForeignPtr и length информацию из Vector (код ниже). Компилятор GHC жалуется на то, что экземпляр Storable не определен для типа ForeignPtr Elems. Мой вопрос: возможно ли сохранить ptr в Vector в определении Storable, без необходимости писать определение экземпляра Storable для ForeignPtr.

Из документации Haddocs , ForeignPtr кажется просто Ptr с назначенным ему финализатором:

Существенная разница между ForeignPtrs и ванильной памятью Ссылки типа Ptr a в том, что первый может быть связан с финализаторы.

Я не хочу обходить проблему, используя Ptr вместо ForeignPtr, из-за проблем, связанных с ее завершением. Поэтому я предпочитаю хранить расположение ForeignPtr (через Ptr (ForeignPtr a)), чтобы сборщик мусора GHC знал о ссылке на него. Но такой подход заставил бы меня определить Storable instance (из-за ограничения (Storable a) => Ptr a, которое имеет смысл).

Есть ли способ сохранить и извлечь ptr для вектора в Storable, не задавая экземпляр Storable для ForeignPtr? Если нет, то написание Storable определения 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 Ответ

3 голосов
/ 22 декабря 2011

ForeignPtr s нельзя сделать Storable, потому что их реализация требует способа связать один или несколько указателей финализатора с необработанным указателем, и эта связь зависит от времени выполнения.Чтобы сделать ForeignPtr хранимым, вам нужно сохранить связанный Ptr (что легко) и массив связанных финализаторов (что невозможно, поскольку финализаторы являются внутренними во время выполнения и могут связываться с GC GHC).время выполнения).

Однако это не та проблема, которую нужно здесь решать.

Проблема в том, что нет разумного способа превратить что-либо, содержащее Vector, во что-то Storable.Vector требует управляемой памяти для своего содержимого (определение Storable.Vector равно data Vector a = Vector Int (ForeignPtr a) плюс некоторые аннотации строгости), но вся цель Storable состоит в сохранении некоторого значения в неуправляемая память.Кроме того, Vector использует различные объемы памяти в зависимости от ее длины, но Storable структуры данных должны использовать постоянный объем памяти.

Вам нужнопереосмыслить то, что ваша структура данных пытается моделировать.Вы действительно должны хранить Vector как это?Помните, что вы храните Vector из Elems, что означает, что вы можете иметь значение T, которое содержит Vector, которое содержит T, которое содержит 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)

Указатель на некоторую Elems использует постоянную память и может указывать на неуправляемую память, так что вы можете создавать сохраняемые экземплярыза это.

...