如何使用Haskell和FFI与C枚举接口?

mca*_*dre 18 enums haskell ffi

假设charm.c有一个枚举key和一个get_key()返回key类型值的函数.

如何公开相应的Haskell Key记录和功能getKey :: IO Key

如何在不手动指定每个枚举值如何映射到Haskell值的情况下执行此操作?

Joh*_*n L 5

对于@KevinReid,这是一个如何使用c2hs执行此操作的示例.

鉴于key文件中的枚举charm.h(我不知道枚举中有什么,所以我只填写了几个值)

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

key get_key();
Run Code Online (Sandbox Code Playgroud)

您可以像这样使用c2hs的枚举钩子:

{#enum key as Key {underscoreToCase} deriving (Eq, Show)#}
Run Code Online (Sandbox Code Playgroud)

要绑定到函数,您可以使用callfun. call更简单,但不做任何编组.以下是两者的示例.ffi-wrapped get_key将返回一个CInt,因此您需要手动call封送它(如果使用)或指定编组(如果使用fun).c2hs不包括enum marshallers所以我在这里写了自己的:

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
Run Code Online (Sandbox Code Playgroud)

C2hs将从此生成以下Haskell(略微清理):

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)
Run Code Online (Sandbox Code Playgroud)