2011-01-25 6 views
8

Estoy intentando crear un contenedor Haskell para una biblioteca C. Las estructuras subyacentes son demasiado complicadas para expresarlas como tipos explícitos, y en realidad no las utilizo más que para pasar de una función C a otra, así que estoy usando EmptyDataDecls para dejar que GHC lo resuelva.Declaración de datos vacía almacenable

Lo que necesito es un puntero a uno de estos tipos de datos, pero cuando intento crear uno con alloca se queja de que los datos no son del tipo Storable. Por ejemplo:

{-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls #-} 

module Main where 

import Foreign.Marshal.Alloc 
import Foreign.Ptr 

data Struct 

foreign import ccall "header.h get_struct" 
    get_struct :: Ptr Struct -> IO() 

main = alloca $ \ptr -> get_struct ptr 

GHC no se compilará esto, diciendo que no hay instancia para Storable Struct. Yo podría aplicar a mí mismo:

instance Storable Struct where 
    sizeOf _ = ... 
    alignment _ = ... 

Pero eso viene cerca de derrotar el propósito - Yo no quiero tener que definir tales cosas si no me importa lo que está en la estructura.

Me he dado cuenta de que un puntero a un puntero funciona bien, porque la clase Ptr es Storable. Por lo que puedo lograr lo que estoy apuntando utilizando peek en ptr antes de llamar get_struct:

main = alloca $ \ptr -> do 
    ptr <- peek ptr 
    get_struct ptr 

Esto se siente como un truco, sin embargo.

¿Hay alguna forma de obtener declaraciones de datos vacías que se consideren Storable sin definir una instancia?

+2

Esto es un truco. Nunca se asigna espacio para el puntero interno; solo estás apuntando a la memoria al azar. De esta manera yace segfaults. –

+0

Entonces, si legítimamente quiero un puntero a un puntero, debería usar dos llamadas 'alloca' y luego' poke' un puntero al otro, ¿verdad? – zmthy

+0

sí (o use alguna otra forma de asignación). Los usos más comunes para un puntero a un puntero son los valores de puntero, en cuyo caso la función enlazada realiza la asignación y las matrices de cadenas (pense 'argv'). Th –

Respuesta

6

No puede asignar algo si no sabe qué tan grande es. ¿La función simplemente va a ignorar su argumento? Luego pase un puntero nulo. De lo contrario, necesita asignar espacio suficiente para la estructura. No corte las esquinas asignando un búfer de cero bytes o de tamaño de puntero, ya que la función llamada escribirá más allá del final de su búfer, corrompiendo la memoria.

Termine la declaración de datos o escriba una instancia Almacenable con los valores de tamaño y alineación adecuados; no hay forma de proporcionar datos de tamaño/alineación de alguna forma.

+2

Puede usar una herramienta conveniente como chs para este propósito - le ayuda a entender estos valores. – fuz

+3

Tenga en cuenta que no tiene que definir todos los métodos de una instancia. Si no desea ordenar las estructuras de Haskell, solo defina 'sizeOf' y' alignment' pero deje 'peek' y' poke' indefinido (o error de llamada). –

2

Aquí hay otro enfoque que podría funcionar para usted. Supongo que tiene acceso a todos los archivos de encabezado C que definen los objetos que necesita asignar. Si eso es cierto, podría escribir una capa delgada de código C para asignar y liberar los objetos C. Su código Haskell puede llamar a estas funciones C sin que el código Haskell tenga que saber qué hay detrás de los punteros. Haskell también puede llamar automáticamente el código libre cuando el recolector de basura de Haskell sabe que los objetos ya no son necesarios.

Cuestiones relacionadas