{-# LANGUAGE TemplateHaskell #-} module System.Linux.Ptrace.GenStruct where import Control.Monad import Foreign import Language.Haskell.TH genStruct :: String -> [String] -> Q Type -> Q [Dec] genStruct name ctors elemType = do let name' = mkName name elemType' <- elemType varsAndTypes <- mapM (\n -> varStrictType (mkName n) (strictType notStrict elemType)) ctors let typeDecl = DataD [{-context-}] name' [{-tyvars-}] [RecC name' varsAndTypes] [{-deriving-}''Show] -- Could evaluate this now, but what happens if we're cross-compiling? Is CInt the target's size, or ours? let elemSize = [|sizeOf (undefined :: $(elemType))|] -- This exposes at least two GHC bugs: -- 1) It's rejected because GHC thinks $(conT name') is a type variable -- 2) The error message reverses the order of member definitions --storableInst <- -- [d|instance Storable $(conT name') where -- sizeOf _ = $(litP . integerL $ length ctors) * $(elemSize) -- alignment _ = alignment (undefined :: $(elemType)) -- peek p = foldl (\e k -> [| $e `ap` peekByteOff p (k * $(elemSize)) |]) [|return $(conE name')|] [0..length ctors-1] -- |] -- This exposes another GHC bug: -- 3) We can't capture Storable in a type quotation since it's a class name. --storableInst <- instanceD (cxt []) [t|Storable $(conT name')|] ... -- Work around instanceD's nasty interface let fixDecs :: Q [Dec] -> Q [DecQ] fixDecs decs = (fmap.fmap) return decs -- Eek, can't substitute this below: TH lifting is not referentially transparent let numCtors = length ctors storableInst <- instanceD (cxt []) (appT (conT ''Storable) (conT name')) =<< fixDecs [d| sizeOf _ = numCtors * $(elemSize) alignment _ = alignment (undefined :: $(elemType)) peek p = $(foldl (\e k -> [| $e `ap` peekByteOff p (k * $(elemSize)) |]) [|return $(conE name')|] [0..length ctors-1]) poke p v = sequence_ $(listE $ map (\(n,c) -> [| pokeByteOff p (n * $(elemSize)) ($(varE (mkName c)) v) |]) $ zip [0::Int ..] ctors) |] return [typeDecl, storableInst]