2011-07-14 24 views
17

Digamos que charm.c tiene una enumeración key y una función get_key() que devuelve un valor de tipo key.¿Cómo interactúa uno con C enum usando Haskell y FFI?

¿Cómo puedo exponer un registro y función de Haskell Key correspondiente getKey :: IO Key?

¿Y cómo puedo hacer esto sin especificar manualmente cómo cada valor de enumeración se correlaciona con un valor de Haskell?

+0

hsc2hs tiene una macro para esto, pero prefiero usar vainilla Haskell si es posible. – mcandre

+5

No puede hacerlo en Haskell vainilla sin escribirlo todo de forma manual. Tanto hsc2hs como c2hs tienen enum hooks, y bindings-dsl usa macros de CPP. Estos generarán automáticamente el tipo de Haskell y la instancia de Enum para ti, pero no es Haskell vainilla. Por supuesto, podrías escribirlo, por ejemplo. c2hs, ejecute el preprocesador, luego envíe solo el archivo Haskell resultante, pero eso a menudo causará problemas con p. alineación y tamaños de ptr. –

+9

La razón por la que necesita herramientas pesadas para hacer esto es que las constantes enum son una construcción en tiempo de compilación en C, no son símbolos exportados que pueden recuperarse mediante el enlace a la biblioteca, por lo que descubrir el valor de enums requiere analizar el texto fuente C. . –

Respuesta

6

Para @KevinReid, he aquí un ejemplo de cómo hacerlo con c2hs.

Dada la enumeración key en el archivo charm.h (no tengo ni idea de lo que hay en la enumeración, por lo que acaba de llenar en unos pocos valores)

typedef enum 
    { 
    PLAIN_KEY = 0, 
    SPECIAL_KEY = 1, 
    NO_KEY = 2 
    } 
key; 

key get_key(); 

Puede utilizar el gancho de enumeración de c2hs así:

{#enum key as Key {underscoreToCase} deriving (Eq, Show)#} 

Para enlazar a una función, puede usar call o call o fun. call es más simple, pero no hace ninguna clasificación. Aquí hay ejemplos de ambos. El get_key envuelto en ffi devolverá un CInt, por lo que debe ordenarlo manualmente (si usa call) o especificar el marcador de referencias (si usa fun). c2hs no incluye señaleros de enumeración por lo que he escrito mi propio aquí:

module Interface where -- file Interface.chs 

{#enum key as Key {underscoreToCase} deriving (Eq, Show)#} 

getKey = cIntToEnum `fmap` {#call get_key #} 

{#fun get_key as getKey2 { } -> `Key' cIntToEnum #} 

cIntToEnum :: Enum a => CInt -> a 
cIntToEnum = toEnum . cIntConv 

C2hs generará la siguiente Haskell de esta (ligeramente limpiado):

data Key = PlainKey 
     | SpecialKey 
     | NoKey 
     deriving (Eq,Show) 
instance Enum Key where 
    fromEnum PlainKey = 0 
    fromEnum SpecialKey = 1 
    fromEnum NoKey = 2 

    toEnum 0 = PlainKey 
    toEnum 1 = SpecialKey 
    toEnum 2 = NoKey 
    toEnum unmatched = error ("Key.toEnum: Cannot match " ++ show unmatched) 

getKey = cIntToEnum `fmap` get_key 

getKey2 :: IO (Key) 
getKey2 = 
    getKey2'_ >>= \res -> 
    let {res' = cIntToEnum res} in 
    return (res') 

cIntToEnum :: Enum a => CInt -> a 
cIntToEnum = toEnum . cIntConv 

foreign import ccall safe "foo.chs.h get_key" 
    get_key :: (IO CInt) 

foreign import ccall safe "foo.chs.h get_key" 
    getKey2'_ :: (IO CInt) 
+0

Parece que las enumeraciones definidas con enum hook ahora tienen un marcador predeterminado: https://github.com/haskell/c2hs/wiki/Implementation%20of%20Haskell%20Binding%20Modules. – HaskellElephant

Cuestiones relacionadas