Skip to content

Commit 0f26f4a

Browse files
authored
Merge pull request #13 from MatrixAI/ffi
Added FFI example using multiple C objects and headers
2 parents d6da46a + 6c73f4a commit 0f26f4a

File tree

8 files changed

+101
-2
lines changed

8 files changed

+101
-2
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,20 @@ Basically remember these:
122122
:module ModuleName
123123
```
124124

125+
## FFI
126+
127+
This project also demonstrates how to use the FFI. C source files are located in `csrc`, while the C headers are in `include`.
128+
129+
The relevant attributes of `package.yaml` are `c-sources`, `include-dirs` and `install-includes`.
130+
131+
The `c-sources` be a list of C files that need to be compiled into objects that are linked during compilation.
132+
133+
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.
134+
135+
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.
136+
137+
Finally you just need to write code like `FFI.hs`, and everything just works normally.
138+
125139
---
126140

127141
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.

app/Main.hs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
module Main where
22

3-
import Lib
3+
import qualified FFI as F
4+
import Lib (someFunc)
45

56
main :: IO ()
6-
main = someFunc
7+
main = do
8+
time <- F.getTime
9+
let pi = F.getPi
10+
let negPi = F.getNegPi
11+
let sum = F.negAdd 1 2
12+
putStrLn $ show time
13+
putStrLn $ show pi
14+
putStrLn $ show negPi
15+
putStrLn $ show sum
16+
someFunc

csrc/algebra.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include "mathematics.h"
2+
3+
int neg_add(int x, int y) {
4+
return add(-x, -y);
5+
}
6+
7+
double get_neg_pi() {
8+
return -get_pi();
9+
}

csrc/mathematics.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
int add(int x, int y) {
2+
return x + y;
3+
}
4+
5+
double get_pi() {
6+
return 3.1415926;
7+
}

include/algebra.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
int neg_add(int x, int y);
2+
3+
double get_neg_pi();

include/mathematics.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
int add(int x, int y);
2+
3+
double get_pi();

package.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,17 @@ dependencies:
1717

1818
library:
1919
source-dirs: src
20+
c-sources:
21+
- csrc/mathematics.c
22+
- csrc/algebra.c
23+
include-dirs:
24+
- include
25+
install-includes:
26+
- mathematics.h
27+
- algebra.h
2028
exposed-modules:
2129
- Lib
30+
- FFI
2231

2332
executables:
2433
haskell-demo-exe:

src/FFI.hs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{-# LANGUAGE ForeignFunctionInterface #-}
2+
3+
module FFI
4+
( getTime
5+
, add
6+
, getPi
7+
, getNegPi
8+
, negAdd
9+
) where
10+
11+
import Foreign.C
12+
import Foreign.Ptr (Ptr, nullPtr)
13+
import Prelude hiding (sin)
14+
15+
-- |A pure stdlib foreign function
16+
foreign import ccall "sin" c_sin :: CDouble -> CDouble
17+
sin :: Double -> Double
18+
sin d = realToFrac (c_sin (realToFrac d))
19+
20+
-- |An impure stdlib function
21+
foreign import ccall "time" c_time :: Ptr a -> IO CTime
22+
getTime :: IO Int
23+
getTime = c_time nullPtr >>= (return . fromEnum)
24+
25+
-- |A pure custom function
26+
foreign import ccall "add" c_add :: CInt -> CInt -> CInt
27+
add :: Int -> Int -> Int
28+
add x y = fromIntegral $ c_add (fromIntegral x) (fromIntegral y)
29+
30+
-- |Another pure custom function
31+
foreign import ccall "get_pi" c_get_pi :: CDouble
32+
getPi :: Double
33+
getPi = realToFrac c_get_pi
34+
35+
-- |Another pure custom function demonstrating the usage of multiple C sources
36+
foreign import ccall "get_neg_pi" c_get_neg_pi :: CDouble
37+
getNegPi :: Double
38+
getNegPi = realToFrac c_get_neg_pi
39+
40+
-- |Another pure custom function demonstrating the usage of multiple C sources
41+
foreign import ccall "neg_add" c_neg_add :: CInt -> CInt -> CInt
42+
negAdd :: Int -> Int -> Int
43+
negAdd x y = fromIntegral $ c_neg_add (toEnum x) (toEnum y)
44+

0 commit comments

Comments
 (0)