2012-03-24 10 views
21

Sono quasi sicuro che è possibile inviare array attraverso l'FFI, ma non riesco a trovare alcun esempio. Ad esempio, ho una matrice Haskell che invio a una funzione int foo(int*) oppure ho un array C int bar[64]; che invio a Haskell.La FFI può gestire gli array? Se é cosi, come?

Idealmente mi piacerebbe il modo più efficiente - Non voglio alcuna allocazione di heap o copia non necessaria. Inoltre, sarebbe bello se potessi usare gli array Unboxed di Haskell sia in Haskell che in C. Quindi, qual è il metodo per farlo?

+0

Sede [ 'Foreign.Marshal.Array'] (http://hackage.haskell.org/ pacchetto/base-4.7.0.0/docs/estero-maresciallo-Array.html). – MasterMastic

risposta

18

Se si utilizza la libreria Data.Vector, è possibile utilizzare Data.Vector.Storable per le proprie esigenze. Quindi è possibile utilizzare funzioni come unsafeToForeignPtr o unsafeWith per accedere al puntatore esterno sottostante. Ciò consente di chiamare il codice C senza effettuare alcuna copia o marshalling.

Se si desidera creare un vettore da un array C, è possibile utilizzare unsafeFromForeignPtr.

Per i vostri esempi che è possibile utilizzare (ammesso c_foo non modificarlo di argomenti)

import Foreign.Ptr 
import Foreign.C.Types 
import System.IO.Unsafe (unsafePerformIO) 
import qualified Data.Vector.Storable as SV 

foreign import ccall unsafe "foo" c_foo :: Ptr CInt -> CInt 

haskellFoo :: SV.Vector CInt -> CInt 
haskellFoo sv = unsafePerformIO $ 
    SV.unsafeWith sv $ \ptr -> return (c_foo ptr) 

questo può essere essere giocato a golf a:

haskellFoo sv = unsafePerformIO $ 
    SV.unsafeWith sv (return . c_foo) 

Nota che se il C-funzione modifica i dati , quindi non si dovrebbe fare questo, invece si dovrebbe fare una copia dei dati per non rompere la trasparenza referenziale.

Se si desidera utilizzare il tipo di array standard, è possibile utilizzare withStorableArray da Data.Array.Storable allo stesso modo.

+0

La tua versione golfata non usa il parametro 'sv', suppongo che tu l'abbia appena accidentalmente omesso (ho provato a modificare la tua risposta ma SO ha insistito che la mia modifica fosse ≥ 6 caratteri :() –

+1

@benmachine: risolto quello per te :) – ehird

+0

@ehird, benmachine: grazie per la modifica. – dnaq

10

Le specifiche FFI sono abbastanza leggibili, quindi è meglio sedersi e lavorare su tutto. Tuttavia, per questa domanda specifica, puoi saltare alla sezione "Marshalling", in particolare le sottosezioni Ptr e Storable, che descrivono ciò che è disponibile per questo.

3

Come in C, un array è fondamentalmente un puntatore al primo membro dell'array. Ottieni gli altri elementi eseguendo l'aritmetica sul puntatore. Il Ptr è un membro di Num, quindi è possibile utilizzare le normali operazioni aritmetiche.

+5

Vero, ma l'aritmetica del puntatore Haskell funziona in * byte *. Quindi in C, dove si scrive 'aPtr + = 1' per passare all'elemento successivo di un array, in Haskell è necessario scrivere' next = aPtr + 1 * sizeOf element'. Oppure puoi usare 'Foreign.Marshal.Array.advancePtr'. –

+0

Sì. Questo è ovviamente vero. – fuz

+0

Non vedo un'istanza di 'Num' per' Ptr' quando impongo 'Foreign' o' Foreign.Ptr', da dove lo ottieni? –

Problemi correlati