1

我正在将 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 预处理器。

4

2 回答 2

7

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
于 2012-05-08T18:15:39.537 回答
3

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

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

RWH的一个例子:

在此处输入图像描述

于 2012-05-08T18:15:10.853 回答