以下の形式のデータ構造があります (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
コンストラクタなし) のカスタム ストアブル定義を作成しました。T
次に、使用するためのカスタム peek と poke 定義を追加しようとしましForeignPtr
たlength
(Vector
コードは以下にあります)。GHC コンパイラは、型Storable
に対してインスタンスが定義されていないと文句を言いますForeignPtr Elems
。私の質問は、ForeignPtr の Storable インスタンス定義を強制的に記述することなく、Storable 定義で ptr を Vector に格納できるかどうかです。
Haddocs のドキュメントから、ForeignPtr はファイナライザーが割り当てられた単なる Ptr のようです。
ForeignPtrs とタイプ Ptr a の一般的なメモリ参照の本質的な違いは、前者がファイナライザに関連付けられる可能性があることです。
ファイナライズの問題があるため、Ptr
代わりに を使用して問題を回避したくありません。そのため、GHC ガベージ コレクタがそれへの参照を認識できるようForeignPtr
に、ForeignPtr の場所を ( を介して) 格納することを好みます。Ptr (ForeignPtr a)
しかし、そのアプローチでは、 (意味Storable instance
のある制約のため)を定義する必要があります。(Storable a) => Ptr a
ForeignPtr の Storable インスタンスを定義せずに、Storable の Vector に ptr を格納および取得する方法はありますか? ない場合は、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