Определение хранимых для рекурсивной структуры данных с участием векторов

У меня есть структура данных в форме ниже (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 ответ

Решение

ForeignPtrс не может быть сделано 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 использует постоянную память и может указывать на неуправляемую память, поэтому вы можете создавать для нее сохраняемые экземпляры.

Другие вопросы по тегам