如何使用Haskell和FFI与C枚举进行接口交互?

18
假设 charm.c 文件有一个枚举类型 key 和一个返回 key 类型值的函数 get_key()
如何暴露相应的 Haskell 的 Key 记录和函数 getKey :: IO Key
而且,如何在不手动指定每个枚举值如何映射到 Haskell 值的情况下完成这个任务?

hsc2hs 有一个宏可以做到这一点,但如果可能的话,我宁愿只使用原生的 Haskell。 - mcandre
5
如果不手动编写,你无法在普通的Haskell中完成此操作。 hsc2hs和c2hs都有枚举钩子,而bindings-dsl使用CPP宏。这些工具可以自动生成Haskell类型和Enum实例,但这不是普通的Haskell。当然,你可以使用c2hs等工具编写代码,运行预处理器,然后只发布生成的Haskell文件,但这通常会导致一些问题,如对齐和指针大小等。 - John L
9
需要使用重型工具来完成此任务的原因是,在 C 语言中,枚举常量是编译时结构。它们不是可以通过链接库检索的导出符号,因此发现枚举值需要分析 C 源代码文本。 - Kevin Reid
1
@John L:你能把那个写成这个问题的答案吗? - Kevin Reid
1个回答

6

针对 @KevinReid,这里有一个使用 c2hs 的示例。

假设在文件 charm.h 中有枚举类型 key(我不知道枚举中具体包含哪些值,所以只填写了一些值)。

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

key get_key();

您可以像这样使用c2hs的enum钩子:
{#enum key as Key {underscoreToCase} deriving (Eq, Show)#}

要绑定到函数,您可以使用callfuncall更简单,但不执行任何编组操作。以下是两者的示例。 ffi-wrapped get_key将返回CInt,因此您需要手动编组它(如果使用call)或指定编组程序(如果使用fun)。 c2hs不包括枚举编组器,因此我在此处编写了自己的编组器:
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将从以下内容生成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)

似乎使用枚举钩子定义的枚举现在具有默认的编组程序:https://github.com/haskell/c2hs/wiki/Implementation%20of%20Haskell%20Binding%20Modules。 - HaskellElephant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接