Vagif Verdi
Vagif Verdi

Reputation: 4906

newtype Int -> CInt marshaller

I'm writing FFI to pdflib. Pdflib C API has lots of functions that return and/or take various handles (document, page, image, font) as plain Integer (not pointer).

In order to ensure i do not accidentally pass the wrong param to a function i create a bunch of newtypes in the form of:

newtype PdiDoc = PdiDoc Int
newtype PdiPage = PdiPage Int
newtype PdfImage = PdfImage Int
newtype PdfFont = PdfFont Int

Now i need to provide a marshaller for those types.

image2c (PdfImage i) = fromIntegral i
font2c (PdfFont f) = fromIntegral f
pdipage2c (PdiPage i) = fromIntegral i

As you see the marshallers are exactly the same, just for different types.

So my question is, is there some kind of type magic, SYB vodoo trick that i can use to have just one function to marshall all those types, or do i have to write same functions again and again for different newtypes ?

EDIT: I accepted Don's answer, because it solved my problem.

I switched on

GeneralizedNewtypeDeriving 

added

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

to each of my newtypes, and now i can use standard fromIntegral to marshall all of them.

Nathan Howell's answer is also correct one, i upvoted it. But unfortunately his solution would mean giving up on FFI preprocessors like c2hs i am using.

Upvotes: 1

Views: 737

Answers (2)

Nathan Howell
Nathan Howell

Reputation: 4637

GHC's FFI extensions allow using newtypes that wrap FFI primitives. You could change the imported function signatures to use the newtypes and (hopefully) avoid having to unwrap them manually.

{-# 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

Alternatively, the new GHC Generics functionality (available since 7.2.1) allows generic unpacking and repacking of newtypes:

{-# 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

Upvotes: 7

Don Stewart
Don Stewart

Reputation: 137987

You can derive 'Num' for your types using GeneralizedNewtypeDeriving, this helps you a bit with literals and operators.

For the marshalling, I'd use a FFI preprocess, such as hsc2hs, which can automate the wrapping and unwrapping of newtypes.

An example from RWH:

enter image description here

Upvotes: 3

Related Questions