2012-03-24 24 views
21

Estoy bastante seguro de que es posible enviar matrices a través del FFI, pero no puedo encontrar ningún ejemplo. Por ejemplo, tengo una matriz Haskell que envío a una función int foo(int*), o tengo una matriz C int bar[64]; que envío a Haskell.¿Puede el FFI lidiar con matrices? ¿Si es así, cómo?

Lo ideal sería que quisiera la forma más eficiente, no quiero ninguna asignación de montón o copia innecesaria. Además, sería bueno si pudiera usar las matrices no compartidas de Haskell tanto en Haskell como en C. Entonces, ¿cuál es el método para hacerlo?

+0

Ver [ 'Foreign.Marshal.Array'] (http://hackage.haskell.org/ paquete/base-4.7.0.0/docs/Foreign-Marshal-Array.html). – MasterMastic

Respuesta

18

Si utiliza la biblioteca Data.Vector puede usar Data.Vector.Storable para sus necesidades. Luego puede usar funciones como insafeToForeignPtr o insafeWith para acceder al puntero externo subyacente. Esto le permite llamar al código C sin que tenga lugar ninguna copia o clasificación.

Si desea crear un vector a partir de una matriz C, puede utilizar inseguroFromForeignPtr.

Para sus ejemplos que puede utilizar (suponiendo c_foo no modifica sus argumentos)

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) 

Esto puede ser golfed a:

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

Tenga en cuenta que si su C-función modifica los datos , entonces no debe hacer esto, en su lugar debe hacer una copia de los datos para no romper la transparencia referencial.

Si desea utilizar el tipo de matriz estándar, puede usar withStorableArray desde Data.Array.Storable de la misma manera.

+0

Su versión golfed no usa el parámetro 'sv', supongo que simplemente la omitió accidentalmente (traté de editar su respuesta pero TES insistió en que mi edición sea ≥ 6 caracteres :() –

+1

@benmachine: solucionado eso para usted :) – ehird

+0

@ehird, benmachine: gracias por la edición. – dnaq

10

La especificación FFI es bastante legible, por lo que es posible que desee sentarse y trabajar durante todo el proceso. Sin embargo, para esta pregunta específica, puede ir a la sección "Marshalling", en particular a las subsecciones Ptr y Storable, que describen qué hay disponible para esto.

3

Como en C, una matriz es básicamente un puntero al primer miembro de la matriz. Obtienes los otros elementos haciendo aritmética en el puntero. El Ptr es miembro de Num, por lo que puede usar operaciones aritméticas habituales.

+5

Cierto, pero la aritmética del puntero Haskell funciona en * bytes *. Entonces en C, donde escribirías 'aPtr + = 1' para pasar al siguiente elemento de una matriz, en Haskell necesitas escribir' next = aPtr + 1 * sizeOf element'. O puede usar 'Foreign.Marshal.Array.advancePtr'. –

+0

Sí. Eso es por supuesto cierto. – fuz

+0

No veo una instancia de 'Num' para' Ptr' cuando importo 'Foreign' o' Foreign.Ptr', ¿de dónde lo estás obteniendo? –

Cuestiones relacionadas