2011-07-14 24 views
17

Giả sử charm.c có enum key và hàm get_key() trả về giá trị loại key.Làm thế nào để một giao diện với một enum C bằng cách sử dụng Haskell và FFI?

Làm cách nào để hiển thị bản ghi Haskell Key tương ứng và chức năng getKey :: IO Key?

Và làm cách nào tôi có thể thực hiện việc này mà không chỉ định cách mọi giá trị enum riêng lẻ ánh xạ tới giá trị Haskell?

+0

hsc2hs có một macro cho điều này, nhưng tôi chỉ muốn sử dụng vanilla Haskell nếu có thể. – mcandre

+5

Bạn không thể làm điều đó trong vanilla Haskell mà không cần viết tất cả bằng tay. hsc2hs và c2hs đều có móc enum, và bindings-dsl sử dụng các macro CPP. Chúng sẽ tự động tạo kiểu Haskell và cá thể Enum cho bạn, nhưng nó không phải là vanilla Haskell. Tất nhiên bạn có thể viết nó với ví dụ c2hs, chạy bộ tiền xử lý, sau đó chỉ gửi tệp Haskell kết quả, nhưng điều đó thường gây ra sự cố với ví dụ: kích thước liên kết và ptr. –

+9

Lý do bạn cần các công cụ hạng nặng để thực hiện điều này là các hằng số enum là một cấu trúc biên dịch trong C - chúng không được xuất ra bằng cách liên kết tới thư viện - do đó khám phá giá trị của enums yêu cầu phân tích văn bản nguồn C . –

Trả lời

6

Đối với @KevinReid, đây là ví dụ về cách thực hiện việc này với c2hs.

Với enum key trong file charm.h (Tôi không có ý tưởng những gì trong enum, vì vậy tôi chỉ điền vào một vài giá trị)

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

key get_key(); 

Bạn có thể sử dụng móc enum c2hs của như thế này:

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

Để liên kết với một hàm, bạn có thể sử dụng call hoặc fun. call là đơn giản hơn, nhưng không làm bất kỳ marshaling. Dưới đây là ví dụ về cả hai. Các f2i bọc get_key sẽ trả về một CInt, vì vậy bạn cần phải tự sắp xếp nó (nếu sử dụng call) hoặc chỉ định trình soạn thảo (nếu sử dụng fun). c2hs không bao gồm marshallers enum vì vậy tôi đã viết riêng của tôi ở đây:

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 sẽ tạo ra Haskell sau từ này (hơi dọn dẹp):

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

Có vẻ như enums được xác định bằng móc enum bây giờ có một marshaller mặc định: https://github.com/haskell/c2hs/wiki/Implementation%20of%20Haskell%20Binding%20Modules. – HaskellElephant

Các vấn đề liên quan