我正在向FFflib写FFI。 Pdflib C API有许多函数可以返回和/或将各种句柄(文档,页面,图像,字体)作为普通的整数(不是指针)。
为了确保我不会意外地将错误的参数传递给函数,我会以以下形式创建一堆新类型:
newtype PdiDoc = PdiDoc Int
newtype PdiPage = PdiPage Int
newtype PdfImage = PdfImage Int
newtype PdfFont = PdfFont Int
现在我需要为这些类型提供编组。
image2c (PdfImage i) = fromIntegral i
font2c (PdfFont f) = fromIntegral f
pdipage2c (PdiPage i) = fromIntegral i
如你所见,marshallers完全相同,仅适用于不同的类型。
所以我的问题是,是否存在某种类型的魔法,SYB vodoo技巧,我只能使用一个函数来编组所有这些类型,或者我是否必须为不同的新类型一次又一次地编写相同的函数?< / p> 编辑:我接受了唐的回答,因为它解决了我的问题。
我打开了
GeneralizedNewtypeDeriving
添加了
deriving (Eq, Ord, Num, Enum, Real, Integral)
我的每个新类型,现在我可以使用标准的积分来编组所有这些类型。
内森豪威尔的答案也是正确的,我赞同它。但不幸的是,他的解决方案意味着放弃FFI预处理器,比如我正在使用的c2hs。答案 0 :(得分:7)
GHC的FFI扩展允许使用包装FFI原语的newtypes。您可以更改导入的函数签名以使用newtypes,并且(希望)避免手动解包它们。
{-# LANGUAGE ForeignFunctionInterface #-}
module Main where
newtype Foo = Foo Int
foreign import ccall someCall :: Foo -> IO Foo
main :: IO ()
main = do
Foo x <- someCall (Foo 1)
print x
或者,新的GHC Generics功能(自7.2.1开始提供)允许通用的解包和重新打包新类型:
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE TypeFamilies #-}
module Main where
import GHC.Generics
-- use a regular newtype
newtype Foo1 = Foo1 Int deriving (Generic, Show)
-- or with record syntax
newtype Foo2 = Foo2{foo2 :: Int} deriving (Generic, Show)
unpack :: (Generic a, Rep a ~ D1 dc (C1 cc (S1 sc (K1 R kc)))) => a -> kc
unpack = unK1 . unM1 . unM1 . unM1 . from
pack :: (Generic a, Rep a ~ D1 dc (C1 cc (S1 sc (K1 R kc)))) => kc -> a
pack = to . M1 . M1 . M1 . K1
-- the C import uses Ints
foreign import ccall "someCall" c'someCall :: Int -> IO Int
-- and the typed wrapper packs/unpacks to FFI primitives
someCall :: Foo1 -> IO Foo2
someCall = fmap pack . c'someCall . unpack
main :: IO ()
main = do
Foo2 x <- someCall (Foo1 1)
print x
答案 1 :(得分:3)
您可以使用GeneralizedNewtypeDeriving
为您的类型派生'Num',这有助于您使用文字和运算符。
对于编组,我会使用FFI预处理,例如hsc2hs,它可以自动包装和展开新类型。
示例from RWH: