2014-06-07 26 views
4

我使用Haskell的FFI與一個C庫,其限定了許多含構件,其是指向double S,旨在被視爲struct類型的的double小號陣列:哈斯克爾Data.Vector.Storable.unsafeFromForeignPtr用C結構的指針/陣列字段

typedef struct Foo { 
    int length; 
    double* values; 
} FooT; 

在我的Haskell綁定到該庫我有一個等效的數據類型,其中我試圖使用Data.Vector.Storable.Vector Double爲陣:

data Foo = Foo { 
    length :: Int, 
    values :: Data.Vector.Storable.Vector Double 
} deriving (Show, Eq) 

爲了ŧ o C庫和我的Haskell代碼之間的數據,當然,我必須爲這些類型編寫Storable實例。我試圖找出一種方法,使用Data.Vector.Storable.unsafeFromForeignPtr從C庫已經分配並填充到堆上的double*數組中創建Haskell Vector。我希望通過這樣做,我可以避免複製double*數組的內容,並且只需將Vector作爲包裝在數組上的一種包裝。 (側面的問題是:鑑於double*陣列可以高達10000s的double秒,這是值得追求這種非複製?)

這是我迄今爲止。我使用hsc2hs宏來幫助生成Storable peek實現:

instance Storable Foo where 
    alignment _ = alignment (undefined :: CDouble) 
    sizeOf _ = #{size FooT} 

    peek ptr = do 
    len <- (#peek FooT, length) ptr 
    valuesField <- ((#peek FooT, values) ptr) :: IO (ForeignPtr Double) 
    let values' = DV.unsafeFromForeignPtr0 valuesField len 

    return Foo { length = len, values = values' } 

    poke ptr (Foo len values') = do 
    (#poke FooT, length) ptr len 
    DV.unsafeWith values' (\ptrValues -> (#poke FooT, values) ptr ptrValues) 

所以在我peek我試圖#peekvalues成員作爲ForeignPtr Double,我可以再與unsafeFromForeignPtr使用。然而,#peek產生這樣的代碼:

valuesField <- (((\ hsc_ptr -> peekByteOff hsc_ptr 16)) ptr) :: IO (ForeignPtr Double) 

,並卡住,因爲沒有Storable實例ForeignPtr Double。我認爲如果我試圖爲ForeignPtr Double實現一個實例,那麼我只會將如何訪問struct成員的地址值的問題轉換爲該實例的peek實現。

因此,總之,我怎樣才能以這樣的方式訪問地址值(即指針)struct成員,以便我可以將它用作參數unsafeFromForeignPtr

回答

1

我不知道如何使用hsc2hs,但是您已經有了一個指向peek中的數據的指針,所以您只需使用該指針,並使用正確的偏移量。免責聲明:這個編譯,但沒有經過測試。

import Data.Vector.Storable (Vector, unsafeFromForeignPtr) 
import Foreign.Storable (Storable (..)) 
import Foreign.C.Types 
import Foreign.ForeignPtr (newForeignPtr_) 
import Foreign.Ptr 

instance Storable Foo where 
    peek ptr = do 
    len <- peek (castPtr ptr) 
    valsPtr <- newForeignPtr_ (castPtr ptr `plusPtr` (sizeOf (undefined :: CInt))) 
    return $ Foo len $ unsafeFromForeignPtr valsPtr 0 len 
+0

看起來'newForeignPtr_'在這裏很重要。我無法手動計算'plusPtr'右手參數的工作方式,但將'newForeignPtr_'與'hsc2hs'的'#peek'生成的指針一起使用。 – ironchicken

+0

我已經使用這個建議編寫了一個答案,但我不能發佈它,直到後來,因爲我太n00b和互聯網不相信我: - /希望我仍然會被打擾發佈它... – ironchicken

+0

@ rjlewis如果你願意,可以在底部用你的解決方案編輯我的答案。我應該可以接受編輯。 – user2407038

0

使用user2407038的的newForeignPtr_建議工作的解決方案是:

instance Storable Foo where 
    alignment _ = alignment (undefined :: CDouble) 
    sizeOf _ = #{size FooT} 

    peek ptr = do 
    len' <- fmap fromIntegral (((#peek FooT, len) d) :: IO CInt) 

    valuesField <- ((#peek FooT, values) ptr) :: IO (Ptr Double) 
    valuesPtr <- newForeignPtr_ valuesField 
    let values' = DV.unsafeFromForeignPtr0 valuesField len 

    return Foo { length = len, values = values' } 

我發現,剛#peek荷蘭國際集團的int場是給我的垃圾而明確聲明它是CInt,然後用fromIntegral把它轉換成Int給了我正確的價值。然後我實際上有一個正確的長度與unsafeFromForeignPtr一起使用。然後,我也用double*指針做了類似的事情:明確地聲明它是Ptr Double

在我的實際應用中的一些struct S的也有它,類似於int領域,我發現char*類型字段也剛剛走出垃圾的#peek後。同樣,明確地聲明它們的類型爲CString也解決了這個問題。