Подтвердить что ты не робот

Как взаимодействовать с C-enum, используя Haskell и FFI?

Скажем, charm.c имеет enum key и функцию get_key(), которая возвращает значение типа key.

Как я могу открыть соответствующую запись и функцию Haskell key getKey :: IO Key?

И как я могу это сделать без указания вручную, как каждое значение enum отображает значение Haskell?

4b9b3361

Ответ 1

Для @KevinReid здесь приведен пример того, как это сделать с помощью c2hs.

Учитывая перечисление key в файле charm.h (я понятия не имею, что в перечислении, поэтому я просто заполнил несколько значений)

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

key get_key();

Вы можете использовать c2hs enum hook следующим образом:

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

Для привязки к функции вы можете использовать либо call, либо fun. call проще, но не делает маршалинга. Вот примеры того и другого. Ffi-wrapped get_key вернет CInt, поэтому вам нужно либо вручную его маршалировать (при использовании call), либо указать маршаллер (при использовании fun). c2hs не включает маршаллеров enum, поэтому я написал здесь свой собственный:

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)