2

以下の形式のデータ構造があります (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 定義を追加しようとしましForeignPtrlength(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
4

1 に答える 1

3

ForeignPtrStorableこれらの実装では、1 つまたは複数のファイナライザー ポインターを生のポインターに関連付ける方法が必要であり、この関連付けはランタイムに依存するため、 を作成することはできません。保存可能にするには、関連付けられたもの (簡単です) と関連付けられたファイナライザーの配列ForeignPtrを保存する必要がありますPtr(ファイナライザーはランタイム内部であり、GHC ランタイムの GC にバインドされる可能性があるため、これは不可能です)。

ただし、これはここで解決する必要がある問題ではありません。

問題は、 a を含むものを something にする合理的な方法がないことVectorですStorable。Aはその内容に対してマネージメモリをVector要求しますが (の定義は、いくつかの厳密性注釈を加えたものです)、 の全体的な目的は、何らかの値をアンマネージメモリに格納することです。さらに、 はその長さに応じて異なる量のメモリを使用しますが、データ構造は一定量のメモリを使用する必要があります。Storable.Vectordata Vector a = Vector Int (ForeignPtr a)StorableVectorStorable

データ構造が何をモデル化しようとしているのかを再考する必要があります。このようなものを本当に保存する必要がありますVectorか?Vectorのを格納していることを思い出してください。つまり、を含む を含むを含むなどElemsの値を保持できます。TVectorTVectorT

代わりに次のデータ構造をモデル化しようとしている可能性があると思いますが、間違っている可能性があります。

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定数メモリを使用し、アンマネージメモリを指すことができるため、格納可能なインスタンスを作成できます。

于 2011-12-21T23:09:58.340 に答える