【问题标题】:newtype Int -> CInt marshaller新类型 Int -> CInt 编组器
【发布时间】:2012-05-10 04:16:54
【问题描述】:

我正在将 FFI 写入 pdflib。 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

如您所见,编组器完全相同,只是针对不同的类型。

所以我的问题是,是否有某种类型的魔法,SYB vodoo 技巧,我可以使用一个函数来编组所有这些类型,还是我必须为不同的新类型一次又一次地编写相同的函数?

编辑:我接受了唐的回答,因为它解决了我的问题。

我打开了

GeneralizedNewtypeDeriving 

添加

deriving (Eq, Ord, Num, Enum, Real, Integral)

到我的每个新类型,现在我可以使用标准 fromIntegral 来编组所有这些。

Nathan Howell 的回答也是正确的,我赞成。但不幸的是,他的解决方案意味着放弃像我正在使用的 c2hs 这样的 FFI 预处理器。

【问题讨论】:

    标签: haskell types


    【解决方案1】:

    GHC 的 FFI 扩展允许使用包装 FFI 原语的新类型。您可以更改导入的函数签名以使用新类型,并且(希望)避免手动解包。

    {-# 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 泛型功能(自 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
    

    【讨论】:

    • 我正在使用 c2hs。它要求我为任何非内置类型提供编组器。有没有办法告诉 c2hs 只使用没有编组器的类型?
    • @VagifVerdi 我从未使用过 c2hs,所以我不确定。看起来他们有一个 newtype 关键字,但它似乎与指针而不是原语一起使用。
    • @VagifVerdi 我也用另一种通用解决方案更新了我的答案。它也不需要 Num/Integral 实例,它们似乎不适合不透明的指针类型。
    • 很酷的解决方案,谢谢。你的绝对是更安全的一个。我想知道那些可怕的打包/解包增加了多少开销?此外,理想的情况是更改 c2hs 以允许在不编组的情况下接受新类型。这种方式根本不需要任何技巧。
    • @VagifVerdi 生成的 STG 代码看起来非常相似(它们被编译掉了)......因此,按照标准衡量,它具有几乎相同的性能也就不足为奇了 - 完全在噪声范围内。
    【解决方案2】:

    您可以使用GeneralizedNewtypeDeriving 为您的类型派生“Num”,这对您使用文字和运算符有所帮助。

    对于编组,我会使用 FFI 预处理,例如 hsc2hs,它可以自动包装和展开新类型。

    一个例子from RWH

    【讨论】:

    • 谢谢,我会检查 GeneralizedNewtypeDeriving。我正在使用 c2hs。它要求我为任何非内置类型提供编组器。因此我的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-26
    • 2012-09-07
    相关资源
    最近更新 更多