Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,20 @@ Basically remember these:
:module ModuleName
```

## FFI

This project also demonstrates how to use the FFI. C source files are located in `csrc`, while the C headers are in `include`.

The relevant attributes of `package.yaml` are `c-sources`, `include-dirs` and `install-includes`.

The `c-sources` be a list of C files that need to be compiled into objects that are linked during compilation.

The `include-dirs` is a list of directories containing C headers to be included. In this case, we have only pointed to `include` because we are only using standard library headers and our own headers. But you can also point to system directories using absolute paths.

The `install-includes` will ensure that these headers (relative to the include-dirs) are also exported to any downstream package that depends on this package. So they can make use of those same headers, if they were also writing their own C code.

Finally you just need to write code like `FFI.hs`, and everything just works normally.

---

Because Haskell is a compiled language, most building tools are `nativeBuildInputs`. However for the `shell.nix` this distinction doesn't matter, because it just puts you into an environment that has all the dependencies.
Expand Down
14 changes: 12 additions & 2 deletions app/Main.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
module Main where

import Lib
import qualified FFI as F
import Lib (someFunc)

main :: IO ()
main = someFunc
main = do
time <- F.getTime
let pi = F.getPi
let negPi = F.getNegPi
let sum = F.negAdd 1 2
putStrLn $ show time
putStrLn $ show pi
putStrLn $ show negPi
putStrLn $ show sum
someFunc
9 changes: 9 additions & 0 deletions csrc/algebra.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include "mathematics.h"

int neg_add(int x, int y) {
return add(-x, -y);
}

double get_neg_pi() {
return -get_pi();
}
7 changes: 7 additions & 0 deletions csrc/mathematics.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
int add(int x, int y) {
return x + y;
}

double get_pi() {
return 3.1415926;
}
3 changes: 3 additions & 0 deletions include/algebra.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
int neg_add(int x, int y);

double get_neg_pi();
3 changes: 3 additions & 0 deletions include/mathematics.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
int add(int x, int y);

double get_pi();
9 changes: 9 additions & 0 deletions package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,17 @@ dependencies:

library:
source-dirs: src
c-sources:
- csrc/mathematics.c
- csrc/algebra.c
include-dirs:
- include
install-includes:
- mathematics.h
- algebra.h
exposed-modules:
- Lib
- FFI

executables:
haskell-demo-exe:
Expand Down
44 changes: 44 additions & 0 deletions src/FFI.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{-# LANGUAGE ForeignFunctionInterface #-}

module FFI
( getTime
, add
, getPi
, getNegPi
, negAdd
) where

import Foreign.C
import Foreign.Ptr (Ptr, nullPtr)
import Prelude hiding (sin)

-- |A pure stdlib foreign function
foreign import ccall "sin" c_sin :: CDouble -> CDouble
sin :: Double -> Double
sin d = realToFrac (c_sin (realToFrac d))

-- |An impure stdlib function
foreign import ccall "time" c_time :: Ptr a -> IO CTime
getTime :: IO Int
getTime = c_time nullPtr >>= (return . fromEnum)

-- |A pure custom function
foreign import ccall "add" c_add :: CInt -> CInt -> CInt
add :: Int -> Int -> Int
add x y = fromIntegral $ c_add (fromIntegral x) (fromIntegral y)

-- |Another pure custom function
foreign import ccall "get_pi" c_get_pi :: CDouble
getPi :: Double
getPi = realToFrac c_get_pi

-- |Another pure custom function demonstrating the usage of multiple C sources
foreign import ccall "get_neg_pi" c_get_neg_pi :: CDouble
getNegPi :: Double
getNegPi = realToFrac c_get_neg_pi

-- |Another pure custom function demonstrating the usage of multiple C sources
foreign import ccall "neg_add" c_neg_add :: CInt -> CInt -> CInt
negAdd :: Int -> Int -> Int
negAdd x y = fromIntegral $ c_neg_add (toEnum x) (toEnum y)