inline-c 0.5.0.1 → 0.5.2.0
raw patch · 6 files changed
+380/−7 lines, 6 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- README.md +346/−0
- changelog.md +2/−0
- inline-c.cabal +2/−1
- src/Language/C/Inline/Context.hs +5/−5
- test/tests.c +12/−0
- test/tests.hs +13/−1
+ README.md view
@@ -0,0 +1,346 @@+# inline-c++[](https://travis-ci.org/fpco/inline-c)++`inline-c` lets you seamlessly call C libraries and embed+high-performance inline C code in Haskell modules. Haskell and C can+be freely intermixed in the same source file, and data passed to and+from code in either language with minimal overhead. No FFI required.++`inline-c` is Haskell's escape hatch (or one of) to the wild world of+legacy code and high-performance numerical and system libraries. It+has other uses too: you can also think of `inline-c` as to Haskell+what inline Assembly is to C — a convenient means to eke out a little+bit of extra performance in those rare cases where C still beats+Haskell.++Build instructions are reserved for the [last section](#how-to-build).+You'll need to compile the examples below and try them out.++## Getting started++Let's say we want to compute the cosine of a number using C from+Haskell. `inline-c` let's you write this function call inline, without+any need for a binding to the foreign function:++```+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++import qualified Language.C.Inline as C++C.include "<math.h>"++main :: IO ()+main = do+ x <- [C.exp| double{ cos(1) } |]+ print x+```++`inline-c` leverages the [quasiquotation][ghc-manual-quasiquotation]+language extension implemented in GHC.+[Template Haskell][ghc-manual-template-haskell] is also required.+Importing the `Language.C.Inline` module brings in scope most required+Haskell definitions. `C.include "<math.h>"` brings into scope the+foreign function `cos()` that we wish to call. Finally, in the `main`+function, `[C.exp| double { cos(1) } |]` denotes an inline C expression+of type `double`. `cexp` stands for "C expression". It is a custom+quasiquoter provided by `inline-c`.++A `C.exp` quasiquotation always includes a type annotation for the+inline C expression. This annotation determines the type of the+quasiquotation in Haskell. Out of the box, `inline-c` knows how to map+many common C types to Haskell type. In this case,++```+[C.exp| double { cos(1) } |] :: IO CDouble+```++For pure C expression like these we also provide `C.pure`, which works+exactly the same but without the `IO`:++```+[C.pure| double { cos(1) } |] :: CDouble+```++Obviously extra care must be taken when using `C.pure`: the embedded C+code must be referentially transparent.++## Multiple statements++`inline-c` allows embedding arbitrary C code, not just expressions, in+the form of a sequence of statements, using the `c` quasiquoter:++```+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++import qualified Language.C.Inline as C++C.include "<stdio.h>"++main :: IO ()+main = do+ x <- [C.block| int {+ // Read and sum 5 integers+ int i, sum = 0, tmp;+ for (i = 0; i < 5; i++) {+ scanf("%d ", &tmp);+ sum += tmp;+ }+ return sum;+ } |]+ print x+```++Just as with `C.exp`, we need a type annotation on the entire C block.+The annotation specifies the return type. That is, the type of the+expression in any return statement.++## Capturing Haskell variables -- parameter declaration++`inline-c` allows referring to Haskell variables inside C expressions+and code blocks. We do so by "anti-quoting" them.++Let's say that we wanted to parameterize the function we wrote above+by how many numbers we should read. We can do so by defining a Haskell+function whose parameter we can refer to from within C:++```+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}+import qualified Language.C.Inline as C+import Foreign.C.Types++C.include "<stdio.h>"++-- | @readAndSum n@ reads @n@ numbers from standard input and returns+-- their sum.+readAndSum :: CInt -> IO CInt+readAndSum n = [C.block| int {+ // Read and sum n integers+ int i, sum = 0, tmp;+ for (i = 0; i < $(int n); i++) {+ scanf("%d ", &tmp);+ sum += tmp;+ }+ return sum;+ } |]++main :: IO ()+main = do+ x <- readAndSum 5+ print x+```++Here, the Haskell variable `n` is captured right where we need it using+`$(int n)`. Standard anti-quotation (we'll talk about additional ones+later) consists of a `$` followed by a C declaration in parenthesis.++For each anti-quotation, a variable with a matching type is expected in+the Haskell environment. In this case `inline-c` expects a variable+named `n` of type `CInt`, which is the case.++## What can be captured and returned?++All C types correspond to exactly one Haskell type. Basic types (`int`,+`long`, `double`, `float`, etc.) get converted to their Haskell+equivalents `CInt`, `CLong`, `CDouble`, `CFloat`. Pointers and arrays+get converted to `Ptr`. Function pointers get converted to `FunPtr`.++`inline-c` can also handle user-defined structs and enums, provided that+they are instances of `Storable` and that you tell `inline-c` about them+using [contexts](#contexts).++## Contexts++Everything beyond the base functionality provided by `inline-c` is+specified in a structure that we call "`Context`". From a user+perspective, if we want to use anything but the default context+(`C.baseCtx`), we must set the `C.Context` explicitly using the+`C.context` function. The next two sections include several examples.++The `C.Context` allows to extend `inline-c` to support++* Custom C types beyond the basic ones;+* And [additional anti-quoters](#more-anti-quoters).++`C.Context`s can be composed using their `Monoid` instance.++Ideally a `C.Context` will be provided for each C library that should be+used with `inline-c`. The user can then combine multiple contexts+together if multiple libraries are to be used in the same program. See+the [`inline-c-nag` package](https://github.com/fpco/inline-c-nag) for+an example of using a `C.Context` tailored for a library.++For information regarding how to define `C.Context`s, see the+Haddock-generated API documentation for `Language.C.Inline.Context`.++## More anti-quoters++Besides the basic anti-quoter, which captures variables as they are,+some more anti-quoters are provided with additional functionality. As+mentioned, `inline-c` can easily be extended with anti-quoters defined+by the user, using [contexts](#contexts).++### Vectors++The `vec-len` and `vec-ptr` anti-quoters in the `C.vecCtx` context let us+easily use [Haskell vectors](http://hackage.haskell.org/package/vector)+in C. Continuing along the "summing" theme, we can write code that sums+Haskell vectors in C:++```+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}+import qualified Language.C.Inline as C+import qualified Data.Vector.Storable.Mutable as V+import Data.Monoid ((<>))+import Foreign.C.Types++-- To use the vector anti-quoters, we need the 'C.vecCtx' along with the+-- 'C.baseCtx'.+C.context (C.baseCtx <> C.vecCtx)++sumVec :: V.IOVector CDouble -> IO CDouble+sumVec vec = [C.block| double {+ double sum = 0;+ int i;+ for (i = 0; i < $vec-len:vec; i++) {+ sum += $vec-ptr:(double *vec)[i];+ }+ return sum+ } |]++main :: IO ()+main = do+ x <- sumVec =<< V.fromList [1,2,3]+ print x+```++The `vec-len` anti-quoter is used simply by specifying the vector we+want to get the length of (in our case, `vec`). To use the `vec-ptr`+anti-quoter it is also required to specify the pointer type we want.+Since `vec` is a vector of `CDouble`s, we want a pointer to `double`s.++## ByteStrings++The `bs-len` and `bs-ptr` ant-quoters in the `C.bsCtx` context work+exactly the same as the `vec-len` and `vec-ptr` counterparts, but with+strict `ByteString`s. The only difference is that it is no necessary to+specify the type of the pointer from C -- it is always going to be+`unsigned char *`:++```+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE QuasiQuotes #-}+import qualified Data.ByteString as BS+import Data.Monoid ((<>))+import Foreign.C.Types+import qualified Language.C.Inline as C++C.context (C.baseCtx <> C.bsCtx)++-- | Count the number of set bits in a 'BS.ByteString'.+countSetBits :: BS.ByteString -> IO CInt+countSetBits bs = [C.block|+ int {+ int i, bits = 0;+ for (i = 0; i < $bs-len:bs; i++) {+ unsigned char ch = $bs-ptr:bs[i];+ bits += (ch * 01001001001ULL & 042104210421ULL) % 017;+ }+ return bits;+ }+ |]+```++### Function pointers++Using the `fun` anti-quoter, present in the `C.funCtx` context, we can+easily turn Haskell function into function pointers.++```+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}+import qualified Language.C.Inline as C++-- To use the function pointer anti-quoter, we need the 'C.funCtx along with+-- the 'C.baseCtx'.+C.context (C.baseCtx <> C.funCtx)++ackermann :: CLong -> CLong -> CLong+ackermann m n+ | m == 0 = n + 1+ | m > 0 && n == 0 = ackermann (m - 1) 1+ | otherwise = ackermann (m - 1) (ackermann m (n - 1))++main :: IO ()+main = do+ let ackermannIO m n = return $ ackermann m n+ let x = 3+ let y = 4+ z <- [C.exp| long{+ $fun:(int (*ackermannIO)(int, int))($(long x), $(long y))+ } |]+ print z+```++In this example, we capture a Haskell function of type `CLong -> CLong+-> IO CLong`, `ackermannIO`, to a function pointer in C, using the `fun`+anti-quoter. Note how we need to specify the function pointer type when+we capture `ackermannIO`, using standard C declaration syntax. Also+note that the `fun` anti-quoter works with `IO` functions, and so we+needed to modify `ackermann` to make it have the right type.++In general, when anti-quoting, if the type can be inferred (like in the+case of `vec-len`), only the Haskell identifier appears. If it can't,+the target C type and the Haskell identifier are mentioned using C+declaration syntax.++## How to build++Each module that uses at least one of the `inline-c` functions gets a C+file associated to it, where the filename of said file will be the same+as the module but with a C extension. This C file must be built after+the Haskell code and linked appropriately. If you use cabal, all you+have to do is declare each associated C file in the `.cabal` file and+you are good.++For example we might have++```+executable foo+ main-is: Main.hs, Foo.hs, Bar.hs+ hs-source-dirs: src+ -- Here the corresponding C sources must be listed for every module+ -- that uses C code. In this example, Main.hs and Bar.hs do, but+ -- Foo.hs does not.+ c-sources: src/Main.c, src/Bar.c+ -- These flags will be passed to the C compiler+ cc-options: -Wall -O2+ -- Libraries to link the code with.+ extra-libraries: -lm+ ...+```++Note that currently `cabal repl` is not supported, because the C code is+not compiled and linked appropriately. Type-checking will still be+performed, so `cabal repl` can still be used to develop.++See `sample-cabal-project` for a working example.++If we were to compile the above manually we could do:++```+$ ghc -c Main.hs+$ cc -c Main.c -o Main_c.o+$ ghc Foo.hs+$ ghc Bar.hs+$ cc -c Bar.c -o Bar_c.o+$ ghc Main.o Foo.o Bar.o Main_c.o Bar_c.o -lm -o Main+```++[ghc-manual-quasiquotation]:+https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/template-haskell.html#th-quasiquotation+[ghc-manual-template-haskell]: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/template-haskell.html
+ changelog.md view
@@ -0,0 +1,2 @@+- 0.5.2.0: Make `bs-ptr` use `char` instead of `unsigned char`. See+ issue #16.
inline-c.cabal view
@@ -1,5 +1,5 @@ name: inline-c-version: 0.5.0.1+version: 0.5.2.0 synopsis: Write Haskell source files including C code inline. No FFI required. description: See <https://github.com/fpco/inline-c/blob/master/README.md>. license: MIT@@ -11,6 +11,7 @@ tested-with: GHC == 7.8.4, GHC == 7.10.1 build-type: Simple cabal-version: >=1.10+Extra-Source-Files: README.md, changelog.md source-repository head type: git
src/Language/C/Inline/Context.hs view
@@ -52,7 +52,7 @@ import qualified Data.Vector.Storable as V import qualified Data.Vector.Storable.Mutable as VM import Foreign.C.Types-import Foreign.Ptr (Ptr, FunPtr, castPtr)+import Foreign.Ptr (Ptr, FunPtr) import Foreign.Storable (Storable) import qualified Language.Haskell.TH as TH import qualified Text.Parser.Token as Parser@@ -377,13 +377,13 @@ { aqParser = do cId <- C.parseIdentifier let s = C.unIdentifier cId- return (s, C.Ptr [] (C.TypeSpecifier mempty (C.Char (Just C.Unsigned))), s)+ return (s, C.Ptr [] (C.TypeSpecifier mempty (C.Char Nothing)), s) , aqMarshaller = \_purity _cTypes cTy cId -> do case cTy of- C.Ptr _ (C.TypeSpecifier _ (C.Char (Just C.Unsigned))) -> do- hsTy <- [t| Ptr CUChar |]+ C.Ptr _ (C.TypeSpecifier _ (C.Char Nothing)) -> do+ hsTy <- [t| Ptr CChar |] hsExp <- getHsVariable "bsCtx" cId- hsExp' <- [| \cont -> BS.unsafeUseAsCString $(return hsExp) $ \ptr -> cont (castPtr ptr) |]+ hsExp' <- [| \cont -> BS.unsafeUseAsCString $(return hsExp) $ \ptr -> cont ptr |] return (hsTy, hsExp') _ -> error "impossible: got type different from `unsigned char' (bsCtx)"
test/tests.c view
@@ -94,3 +94,15 @@ } ++int inline_c_14_17d13d6f2f87c2401476aaf41e06593689723baf(long bs_inline_c_0, char * bs_inline_c_1) {++ int i, bits = 0;+ for (i = 0; i < bs_inline_c_0; i++) {+ char ch = bs_inline_c_1[i];+ bits += (ch * 01001001001ULL & 042104210421ULL) % 017;+ }+ return bits;+ +}+
test/tests.hs view
@@ -19,7 +19,7 @@ import Dummy -C.context (C.baseCtx <> C.funCtx <> C.vecCtx)+C.context (C.baseCtx <> C.funCtx <> C.vecCtx <> C.bsCtx) C.include "<math.h>" C.include "<stdio.h>"@@ -156,3 +156,15 @@ return x; } |] sum' `Hspec.shouldBe` 3 * 10+ Hspec.it "bytestrings" $ do+ let bs = "foo"+ bits <- [C.block| int {+ int i, bits = 0;+ for (i = 0; i < $bs-len:bs; i++) {+ char ch = $bs-ptr:bs[i];+ bits += (ch * 01001001001ULL & 042104210421ULL) % 017;+ }+ return bits;+ } |]+ bits `Hspec.shouldBe` 16+