2011-07-14 14 views
17

Charm.c'nin enum key ve get_key() işlevini içeren bir key tipi değeri döndürdüğünü varsayalım.Haskell ve FFI kullanarak bir C enum ile bir arayüz nasıl?

İlgili bir Haskell Key kaydını ve işlevini nasıl görüntüleyebilirim getKey :: IO Key?

Ve her bir enum değerinin bir Haskell değerine nasıl eşlendiğini manuel olarak belirtmeden bunu nasıl yapabilirim?

+0

hsc2hs bunun için bir makro var, ama mümkünse vanilya Haskell'i kullanmayı tercih ederim. – mcandre

+5

Vanilla Haskell'de el yazmadan bunu yapamazsınız. hsc2hs ve c2hs'in her ikisi de enum kancalarına sahiptir ve bindings-dsl, CPP makrolarını kullanır. Bunlar sizin için Haskell türünü ve Enum örneğini otomatik olarak oluşturacaktır, ancak vanilya Haskell değildir. Tabii ki, ör. c2hs, önişlemciyi çalıştırın, sonra sadece sonuçtaki Haskell dosyasını gönderin, ancak bu genellikle hizalama ve ptr boyutları. –

+9

Bunu yapmak için ağır araçlara gerek duymanızın sebebi, enum sabitlerinin C dilinde bir derleme zamanı yapısı olmasıdır - bunlar kütüphaneye bağlanma yoluyla alınabilen semboller değildir - böylece envanterin değerini bulmak C kaynak metnini analiz etmeyi gerektirir . –

cevap

6

@KevinReid için, bunun c2hs ile nasıl yapılacağı ile ilgili bir örnek.

: dosyasında charm.h yılında enum key (Ben enum hiçbir fikrin var, bu yüzden sadece birkaç girilen değerleri)

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

key get_key(); 

Böyle c2hs en enum kanca kullanabilirsiniz Verilen

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

Bir işleve bağlanmak için call veya fun'u kullanabilirsiniz. call daha basittir, ancak herhangi bir marşlama yapmaz. İşte her ikisinin örnekleri. Ffi-sarılı get_key bir CInt döndürecektir, bu nedenle ya elle (bir call kullanıyorsa) marshaller (fun kullanıyorsa) belirtmeniz gerekir. Burada kendi yazdım böylece c2hs enum marshallers içermez:

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 aşağıdaki (hafifçe temizlenir) bundan Haskell üretecektir:

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

Enum kanca ile tanımlanan enums gibi artık varsayılan bir marshaller vardır: https://github.com/haskell/c2hs/wiki/Implementation%20of%20Haskell%20Binding%20Modules. – HaskellElephant