让我们假设charm.c具有枚举key
和函数get_key()
,该函数返回key
类型的值。如何使用Haskell和FFI与C enum进行交互?
如何才能公开相应的Haskell Key
记录和功能getKey :: IO Key
?
如何在不手动指定每个枚举值如何映射到Haskell值的情况下执行此操作?
让我们假设charm.c具有枚举key
和函数get_key()
,该函数返回key
类型的值。如何使用Haskell和FFI与C enum进行交互?
如何才能公开相应的Haskell Key
记录和功能getKey :: IO Key
?
如何在不手动指定每个枚举值如何映射到Haskell值的情况下执行此操作?
对于@KevinReid,这里是一个如何使用c2hs来做到这一点的例子。
鉴于文件charm.h
在枚举key
(我不知道什么是在枚举,所以我只是填写一些值)
typedef enum
{
PLAIN_KEY = 0,
SPECIAL_KEY = 1,
NO_KEY = 2
}
key;
key get_key();
您可以使用c2hs的枚举挂钩这样的:
{#enum key as Key {underscoreToCase} deriving (Eq, Show)#}
要绑定到一个函数,可以使用call
或fun
。 call
更简单,但不做任何编组。这里是两个例子。 ffi包装的get_key
将返回一个CInt,因此您需要手动封送它(如果使用call
)或指定编组器(如果使用fun
)。 c2hs不包括枚举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
C2hs会产生从这个以下哈斯克尔(略清理):
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)
似乎像使用enum钩子定义的枚举现在有一个默认编组器:https://github.com/haskell/c2hs/wiki/Implementation%20of%20Haskell%20Binding%20Modules。 – HaskellElephant
hsc2hs有这个宏,但如果可能的话,我宁愿只使用vanilla Haskell。 – mcandre
你不能在香草Haskell中做到这一点,而无需手工编写。 hsc2hs和c2hs都具有枚举挂钩,绑定-dsl使用CPP宏。这些将为你自动生成Haskell类型和Enum实例,但它不是Haskell的香草。当然你可以用例如c2hs,运行预处理程序,然后运送生成的Haskell文件,但这往往会导致问题。对齐和ptr大小。 –
您需要重量级工具的原因是枚举常量是C中的编译时构造 - 它们不是导出的符号,可通过链接到库来检索 - 因此,发现枚举值需要分析C源文本。 –