packages feed

C-structs 0.1.0.1 → 0.2.0.1

raw patch · 8 files changed

+118/−72 lines, 8 filesdep ~QuickCheckdep ~test-framework-quickcheck2PVP ok

version bump matches the API change (PVP)

Dependency ranges changed: QuickCheck, test-framework-quickcheck2

API changes (from Hackage documentation)

- Foreign.C.Structs: alignment :: Storable a => a -> Int
- Foreign.C.Structs: castPtr :: () => Ptr a -> Ptr b
- Foreign.C.Structs: class Storable a
- Foreign.C.Structs: peek :: Storable a => Ptr a -> IO a
- Foreign.C.Structs: poke :: Storable a => Ptr a -> a -> IO ()
- Foreign.C.Structs: sizeOf :: Storable a => a -> Int

Files

C-structs.cabal view
@@ -1,11 +1,11 @@ Name: C-structs-Version: 0.1.0.1+Version: 0.2.0.1 Cabal-Version: >= 1.10 License: MIT License-file: LICENSE Author: Simon Plakolb Copyright: (c) 2020 Simon Plakolb-Homepage: https://github.com/pinselimo/cstructs-in-haskell+Homepage: https://github.com/pinselimo/cstructs-in-haskell#readme Synopsis: C-Structs implementation for Haskell Description:     C-structs lets you create correct C structs in Haskell.@@ -61,11 +61,11 @@         C-structs,         base         >= 3.0 && < 5.0,         HUnit        >= 1.2 && < 1.7,-        QuickCheck   >= 2.3 && < 2.15,+        QuickCheck   >= 2.10 && < 2.15,         template-haskell >= 2.2 && < 2.17,         test-framework  >= 0.4.1 && < 0.9,         test-framework-hunit >= 0.2.6 && < 0.4,-        test-framework-quickcheck2 >= 0.2.8 && < 0.4+        test-framework-quickcheck2 >= 0.3.0.4 && < 0.4  Test-Suite doctest     type: exitcode-stdio-1.0
CHANGELOG.md view
@@ -1,2 +1,6 @@+v0.2.0.1: Remove re-exports of Foreign.C.Storable and Foreign.C.Ptr members++v0.1.0.2: Better Haddock and a fixed C-test template for GHC < 8.0+ v0.1.0.1: Added thorough testing and structs with up to four fields 
README.md view
@@ -7,7 +7,6 @@  Note: As of GHC 8.10 structs cannot be passed by value, [only by reference](https://wiki.haskell.org/Foreign_Function_Interface#Foreign_types). - ## Usage  You can use these types as a classic ```hackage``` package.@@ -107,6 +106,16 @@ instance Storable Struct8 ... ~~~ +### Accessors++The naming scheme of the accessor functions follows the names of the ordinal numbers. This can be inconvenient in a Template Haskell context. For these situations ```Foreign.C.Structs``` exposes the ```acs``` function:++~~~haskell+$(acs 8 2)+~~~++This expression will be spliced into a function taking a ```Struct8``` and extracting its second field.+ ## Testing  Identity properties are tested with QuickCheck to ensure that peek and poke are reversible.@@ -114,7 +123,7 @@ The ```alignment``` function is trivial and only tested implicitly through ```sizeOf```.  Imports from C are tested in ```CTest.hs``` and together with the identity tests form the guarantee that also exports to C are consistent.-All tests are performed for all available GHC versions through [haskell-ci](https://github.com/haskell-CI/haskell-ci) to ensure maximum compatibility.+All tests are performed for all available GHC/CABAL/Stack versions through the [Stack CI script](https://docs.haskellstack.org/en/stable/travis_ci/) on both Linux and OSX to ensure maximum compatibility.  ## License 
src/Foreign/C/Structs.hs view
@@ -7,6 +7,30 @@ Stability       : beta  The @Foreign.C.Structs@ module allows you to construct C structs of arbitrary @Storable@ types.+It also defined them as instances of the Storable type-class. You can thus create pointers+to an instance of such a struct and interface with another language.++Currently up to six records are supported. Each number of records needs its own type.+The types are named after the number of records they support: 'Struct2', 'Struct3' .. @StructN@++If a Struct type with more fields is required, it can be created using Template Haskell and the 'structT' function:++> structT 8 -- creates a Struct with 8 fields++Field access is provided threefold:++ * Record syntax++> 2nd :: Struct2 a b -> b++ * Pattern matching++> (Struct2 a b)++ * Template Haskell 'acs' function.++> $(acs 2 2) :: Struct2 a b -> b+ -} module Foreign.C.Structs (       Struct2(..)@@ -18,52 +42,24 @@     , acs     -- Exports for Template Haskell usage     , next, sizeof, fmax-    -- Reexports for Template Haskell-    , Storable, peek, poke, sizeOf, alignment, castPtr     ) where+ import Foreign.C.Structs.Types (-     Struct2(..)-    ,Struct3(..)-    ,Struct4(..)-    ,Struct5(..)-    ,Struct6(..)+      Struct2(..)+    , Struct3(..)+    , Struct4(..)+    , Struct5(..)+    , Struct6(..)     )  import Foreign.C.Structs.Templates (-     structT-    ,acs+      structT+    , acs     ) -import Foreign.Storable (-     Storable, peek, poke, sizeOf, alignment-    )-import Foreign.Ptr (-     castPtr-    ) import Foreign.C.Structs.Utils (-     next-    ,sizeof-    ,fmax+      next+    , sizeof+    , fmax     )--{- |-C-Structs------------The @Foreign.C.Structs@ module allows you to construct C structs of arbitrary @Storable@ types.-It also defined them as instances of the Storable type-class. You can thus create pointers-to an instance of such a struct and interface with another language.--Currently up to six records are supported. Each number of records needs its own type.-The types are named after the number of records they support: 'Struct2', 'Struct3' .. @StructN@--If a Struct type with more fields is required, it can be created using Template Haskell and the 'structT' function:--> structT 8 -- creates a Struct with 8 fields--Field access is provided threefold:- * Record syntax- * Pattern matching- * Template Haskell 'acs' function.--} 
src/Foreign/C/Structs/Templates.hs view
@@ -35,25 +35,32 @@ -- acs :: Int -> Int -> ExpQ acs big_n small_n = [| \struct -> $(caseE [| struct |] [m]) |]-    where m :: MatchQ+    where+          m :: MatchQ           m = match pat (normalB $ varE $ vrs !! (small_n-1)) []+           pat :: PatQ           pat = conP str $ map varP $ take big_n vrs+           str = mkName $ "Struct" ++ show big_n+           vrs = fieldnames ""  -- Templating functions  structTypeT :: Int -> Dec #if __GLASGOW_HASKELL__ < 800-structTypeT nfields = DataD [] (sTypeN nfields) tyVars [constructor] deriv''+structTypeT nfields = DataD [] (structType nfields) tyVars [constructor] deriv'' #elif __GLASGOW_HASKELL__ < 802-structTypeT nfields = DataD [] (sTypeN nfields) tyVars Nothing [constructor] deriv'+structTypeT nfields = DataD [] (structType nfields) tyVars Nothing [constructor] deriv' #else-structTypeT nfields = DataD [] (sTypeN nfields) tyVars Nothing [constructor] [deriv]+structTypeT nfields = DataD [] (structType nfields) tyVars Nothing [constructor] [deriv] #endif-    where tyVars    = map PlainTV $ take nfields $ fieldnames ""-          constructor = RecC (sTypeN nfields) $ take nfields records+    where+          tyVars    = map PlainTV $ take nfields $ fieldnames ""++          constructor = RecC (structType nfields) $ take nfields records+           records     = zipWith defRec (getters nfields) (fieldnames "") #if __GLASGOW_HASKELL__ < 800           defRec n t  = (,,) n NotStrict (VarT t)@@ -61,6 +68,7 @@           defRec n t  = (,,) n (Bang NoSourceUnpackedness NoSourceStrictness) (VarT t) #endif           deriv'' = [''Show, ''Eq]+           deriv' = map ConT deriv'' #if __GLASGOW_HASKELL__ > 800           deriv = DerivClause Nothing deriv'@@ -72,14 +80,16 @@ #else storableInstanceT nfields = InstanceD Nothing cxt tp decs #endif-    where vars = take nfields $ fieldnames ""+    where+          vars = take nfields $ fieldnames ""+           storable = AppT $ ConT ''Storable #if __GLASGOW_HASKELL__ < 710           cxt  = map (\v -> ClassP ''Storable [VarT v]) vars #else           cxt  = map (storable . VarT) vars #endif-          tp   = storable $ foldl AppT (ConT $ sTypeN nfields) $ map VarT vars+          tp   = storable $ foldl AppT (ConT $ structType nfields) $ map VarT vars            decs = [ sizeOfT nfields                  , alignmentT nfields@@ -91,52 +101,71 @@  sizeOfT :: Int -> Dec sizeOfT nfields = FunD 'sizeOf [clause]-    where clause = Clause [VarP struct] (NormalB body) wheres+    where+          clause = Clause [VarP struct] (NormalB body) wheres+           body = AppE (AppE (VarE 'sizeof) $ alignments "a") (sizes "s")+           alignments = ListE . take nfields . map VarE . fieldnames+           sizes = ListE . take nfields . map VarE . fieldnames+           wheres = vals 'alignment nfields "a" ++ vals 'sizeOf nfields "s"  alignmentT :: Int -> Dec alignmentT nfields = FunD 'alignment [clause]-     where clause = Clause [VarP struct] (NormalB body) wheres+    where+           clause = Clause [VarP struct] (NormalB body) wheres+            body = AppE (VarE 'fmax) (ListE $ take nfields $ map VarE $ fieldnames "")+            wheres = vals 'alignment nfields ""  peekT :: Int -> Dec peekT nfields = FunD 'peek [clause]     where           vars = take nfields $ fieldnames ""+           ptrs = tail $ take nfields $ fieldnames "_ptr"+           clause = Clause [VarP ptr] (NormalB body) []            body = DoE $ initial ++ concat gotos ++ final+           initial = [ BindS (VarP $ head vars) (AppE (VarE 'peek) castPtr')                     , BindS (VarP $ head ptrs) (AppE (AppE (VarE 'next) $ VarE ptr) $ VarE $ head vars)-                  ]+                    ]++           gotos = zipWith3 goto (tail vars) ptrs (tail ptrs)+           goto n p next_p = [bindVar' p n, bindPtr' next_p p (VarE n)]            final = [ bindVar' (last ptrs) (last vars)-                  , NoBindS $ AppE (VarE 'return) $ foldl AppE (ConE (sTypeN nfields)) (map VarE vars)-                ]+                  , NoBindS $ AppE (VarE 'return) $ foldl AppE (ConE (structType nfields)) (map VarE vars)+                  ]  pokeT :: Int -> Dec pokeT nfields = FunD 'poke [clause]     where           vars = take nfields $ fieldnames ""+           ptrs = tail $ take nfields $ fieldnames "_ptr"+           clause = Clause patterns (NormalB body) [] -          patterns = [VarP ptr, ConP (sTypeN nfields) (map VarP vars)]+          patterns = [VarP ptr, ConP (structType nfields) (map VarP vars)]+           body = DoE $ [init_poke, init_next] ++ concat gotos ++ [final]            init_poke = NoBindS                     $ AppE cast_poke_ptr (VarE $ head vars)                     where  cast_poke_ptr = AppE (VarE 'poke) castPtr'+           init_next = bindPtr' (head ptrs) ptr (VarE $ head vars)            gotos = zipWith3 goto (tail vars) ptrs $ tail ptrs+           goto n p next_p = [pokeVar' p var, bindPtr' next_p p var]                 where var = VarE n @@ -144,25 +173,33 @@  -- Helpers and Constants -sTypeN n = mkName ("Struct" ++ show n)+structType n = mkName ("Struct" ++ show n)+ struct   = mkName "struct"+ ptr      = mkName "ptr"+ castPtr' = AppE (VarE 'castPtr) (VarE ptr)  fieldnames :: String -> [Name] fieldnames s = map (mkName . (:s)) ['a'..'z']+ getters    :: Int -> [Name] getters n = map (mkName . (("s" ++ show n) ++))           $  ["1st","2nd","3rd"]           ++ [show n ++ "th" | n <- [4..]]  vals f n s = take n $ zipWith val (fieldnames s) (getters n)-    where val v getter = ValD (VarP v) (NormalB $ body getter) []+    where+          val v getter = ValD (VarP v) (NormalB $ body getter) []+           body getter  = AppE (VarE f) $ AppE (VarE getter) $ VarE struct  bindVar' ptr var = BindS (VarP var) (AppE (VarE 'peek) $ VarE ptr)+ pokeVar' ptr var = NoBindS        $ AppE (AppE (VarE 'poke) $ VarE ptr) var+ bindPtr' np pp var = BindS (VarP np)        $ AppE next_ptr var        where next_ptr = AppE (VarE 'next) $ VarE pp
src/Foreign/C/Structs/Types.hs view
@@ -16,7 +16,7 @@ import Foreign.Storable (Storable, peek, poke, alignment, sizeOf) import Foreign.Ptr (Ptr, castPtr) -import Foreign.C.Structs.Utils+import Foreign.C.Structs.Utils (next, fmax, sizeof) import Foreign.C.Structs.Templates (structT)  -- | A 'Struct2' can hold two records of any 'Storable' types @a@ and @b@.
src/Foreign/C/Structs/Utils.hs view
@@ -18,22 +18,22 @@  -- | Due to alignment constraints the size of C structs is dependent on the order of fields and their respectible sizes. The function 'sizeof' can calculate the resulting size given a list of all 'alignments' and 'sizes'. sizeof :: [Int] -> [Int] -> Int-sizeof alignments sizes = sizeof' 0 alignments sizes+sizeof as@(_:alignments) (s:sizes) = sizeof' s alignments sizes     where-          sizeof' 0 (a:as) (s:ss) = sizeof' s as ss-          sizeof' s [] [] = s `pad` foldr max 0 alignments+          sizeof' s [] [] = s `pad` fmax as           sizeof' x (a:as) (s:ss) = let-                            s' = x+s-                            in sizeof' (s' `pad` a) as ss+                               s' = x+s+                               in sizeof' (s' `pad` a) as ss -pad x a-  | x `mod` a == 0 = x-  | otherwise      = pad (x+1) a+          pad x a+              | x `mod` a == 0 = x+              | otherwise      = pad (x+1) a  -- | Jumps to the next pointer location in the struct. next :: (Storable a, Storable b, Storable c) => Ptr a -> b -> IO (Ptr c) next ptr x = alloca $ next' ptr x-    where next' :: (Storable a, Storable b, Storable c) => Ptr a -> b -> Ptr c -> IO (Ptr c)+    where+          next' :: (Storable a, Storable b, Storable c) => Ptr a -> b -> Ptr c -> IO (Ptr c)           next' ptr x ptr_x = do                 let ptr_y = plusPtr ptr $ sizeOf x                 y <- peek ptr_x
test/Templates.hs view
@@ -5,7 +5,7 @@ import Language.Haskell.TH.Syntax (Lift) import Foreign.Ptr (Ptr) import Foreign.Marshal.Alloc (free)-import Foreign.Storable (peek)+import Foreign.Storable (peek, sizeOf) import Test.HUnit ((@?=)) import Test.Framework.Providers.HUnit (testCase)