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