on master help sign in
Merge pull request #3070 from jfdm/jmitchell-fix/master/cleanup-deprecated-ffi
78d463eJan de Muijnck-Hughes
docs/guides/type-providers-ffi.rst+30−76
@@ -115,75 +115,19 @@ So now we can get the size of ``size_t`` as long as we're in C code.
115We'd like to be able to use this from Idris. Can we do this? It turns
115We'd like to be able to use this from Idris. Can we do this? It turns
116out we can.
116out we can.
117
117
118−``mkForeign``
118+``foreign``
119-------------
119-------------
120
120
121−With mkForeign, we can turn a C function into an IO action. It works
121+With foreign, we can turn a C function into an IO action. It works
122like this:
122like this:
123
123
124.. code-block:: idris
124.. code-block:: idris
125
125
126 getSizeT : IO Int
126 getSizeT : IO Int
127− getSizeT = mkForeign (FFun "sizeof_size_t" [] FInt)
127+ getSizeT = foreign FFI_C "sizeof_size_t" (IO Int)
128
128
129−Pretty simple. ``mkForeign`` takes a specification of what function it
129+Pretty simple. ``foreign`` takes a specification of what function it
130−needs to call, and we construct this specification with ``FFun``. And
130+needs to call and that function's return type.
131−``FFun`` just takes a name, a list of argument types (we have none),
132−and a return type.
133−
134−One thing you might want to note: the return type we've specified is
135−``FInt``, not ``Int``. That's because ``Int`` is an idris type and C
136−functions don't return idris types. ``FInt`` is not an idris type, but
137−is just the representation of the type of a C int. It tells the
138−compiler "Treat the return value of this C function like it's a C int,
139−and when you pass it back into Idris, convert it to an Idris int."
140−
141−Caveats of mkForeign
142−--------------------
143−
144−First and foremost: ``mkForeign`` is not actually a function. It is
145−treated specially by the compiler, and there are certain rules you
146−need to follow when using it.
147−
148−- Rule 1: the name string must be a literal or constant
149−
150−This does not work:
151−
152−.. code-block:: idris
153−
154− intIntToInt : String -> Int -> Int -> IO Int
155− intIntToInt name = mkForeign (FFun name [FInt, FInt] FInt)
156−
157−You'll just have to bite the bullet and write out the whole
158−``mkForeign`` and ``FFun`` expression each time.
159−
160−- Rule 2: the "call" to ``mkForeign`` must be fully applied
161−
162−This just means that every argument appearing in the list of argument
163−types must be applied wherever you write ``mkForeign``. The arguments
164−don't have to be literals or even known at compile time; they just
165−have to be there. For example, if we have ``strlen : String -> IO
166−Int``, then this is fine:
167−
168−.. code-block:: idris
169−
170− strlen str = mkForeign (FFun "strlen" [FString] FInt) str
171−
172−but this is not fine:
173−
174−.. code-block:: idris
175−
176− strlen = mkForeign (FFun "strlen" [FString] FInt)
177−
178−Note that this only applies to places where you literally typed
179−``mkForeign``. Once you've defined it, ``strlen`` is just a normal
180−function returning an IO action, and it doesn't need to be fully
181−applied. This is okay:
182−
183−.. code-block:: idris
184−
185− lengths : IO [Int]
186− lengths = mapM strlen listOfStrings
187
131
188Running foreign functions
132Running foreign functions
189-------------------------
133-------------------------
@@ -242,6 +186,8 @@ First, let's write a C file containing functions that we'll bind to.
242.. code-block:: c
186.. code-block:: c
243
187
244 /* stattypes.c */
188 /* stattypes.c */
189+ #include <sys/stat.h>
190+
245 int sizeof_dev_t() { return sizeof(dev_t); }
191 int sizeof_dev_t() { return sizeof(dev_t); }
246 int sizeof_ino_t() { return sizeof(ino_t); }
192 int sizeof_ino_t() { return sizeof(ino_t); }
247 /* lots more functions like this */
193 /* lots more functions like this */
@@ -256,20 +202,30 @@ Next, an Idris file to define our providers:
256 %dynamic "./stattypes.so"
202 %dynamic "./stattypes.so"
257
203
258 sizeOfDevT : IO Int
204 sizeOfDevT : IO Int
259− sizeOfDevT = mkForeign (FFun "sizeof_dev_t" [] FInt)
205+ sizeOfDevT = foreign FFI_C "sizeof_dev_t" (IO Int)
260 {- lots of similar functions -}
206 {- lots of similar functions -}
261
207
208+ -- Indicates how many bits are used to represent various system
209+ -- stat types.
210+ data BitWidth = B8 | B16 | B32 | B64
211+
212+ instance Show BitWidth where
213+ show B8 = "8 bits"
214+ show B16 = "16 bits"
215+ show B32 = "32 bits"
216+ show B64 = "64 bits"
217+
262− -- now we have an integer, but we want a Provider FTy
218+ -- Now we have an integer, but we want a Provider BitWidth.
263− -- since our sizeOf* functions are ordinary IO actions, we
219+ -- Since our sizeOf* functions are ordinary IO actions, we
264 -- can just map over them.
220 -- can just map over them.
265− bytesToType : Int -> Provider FTy
221+ bytesToType : Int -> Provider BitWidth
266− bytesToType 1 = Provide (FIntT IT8) -- "8 bit foreign integer"
222+ bytesToType 1 = Provide B8 -- "8 bit value"
267− bytesToType 2 = Provide (FIntT IT16)
223+ bytesToType 2 = Provide B16
268− bytesToType 4 = Provide (FIntT IT32)
224+ bytesToType 4 = Provide B32
269− bytesToType 8 = Provide (FIntT IT64)
225+ bytesToType 8 = Provide B64
270 bytesToType _ = Error "Unrecognised integral type."
226 bytesToType _ = Error "Unrecognised integral type."
271
227
272− getDevT : IO (Provider FTy)
228+ getDevT : IO (Provider BitWidth)
273 getDevT = map bytesToType sizeOfDevT
229 getDevT = map bytesToType sizeOfDevT
274 {- lots of similar functions -}
230 {- lots of similar functions -}
275
231
@@ -282,10 +238,8 @@ providers:
282 module Main
238 module Main
283 import Providers
239 import Providers
284 %language TypeProviders
240 %language TypeProviders
285− %provide (FDevT : FTy) with getDevT
241+ %provide (DevTBitWidth : BitWidth) with getDevT
286−
287− -- interpFTy translates a foreign type to the corresponding idris type
288− DevT : Type
289− DevT = interpFTy FDevT -- on most systems, DevT = Bits64
290
242
291− -- We can now use DevT in our program and FDevT in our FFun expressions!
243+ -- We can now use DevTBitWidth in our program!
244+ main : IO ()
245+ main = putStrLn $ "size of dev_t: " ++ show DevTBitWidth
idris.cabal+4
@@ -791,6 +791,10 @@ Extra-source-files:
791 test/tutorial006/run
791 test/tutorial006/run
792 test/tutorial006/*.idr
792 test/tutorial006/*.idr
793 test/tutorial006/expected
793 test/tutorial006/expected
794+ test/tutorial007/run
795+ test/tutorial007/*.idr
796+ test/tutorial007/*.c
797+ test/tutorial007/expected
794
798
795 test/unique001/run
799 test/unique001/run
796 test/unique001/*.idr
800 test/unique001/*.idr
test/Makefile+1−1
@@ -12,7 +12,7 @@ info: runtest
12 @./runtest $(patsubst %.test,%,$@) -q
12 @./runtest $(patsubst %.test,%,$@) -q
13
13
14test_js: runtest
14test_js: runtest
15− @./runtest without sugar004 reg029 reg052 io001 dsl002 io003 effects001 effects002 basic007 basic011 ffi006 ffi007 ffi008 primitives005 primitives006 opts --codegen node
15+ @./runtest without tutorial007 sugar004 reg029 reg052 io001 dsl002 io003 effects001 effects002 basic007 basic011 ffi006 ffi007 ffi008 primitives005 primitives006 opts --codegen node
16
16
17update: runtest
17update: runtest
18 @./runtest all -u
18 @./runtest all -u
test/ffi004/theOtherType+1−1
@@ -1 +1 @@
1−Nat
1+When `fromFile` reads me it will provide the default type Nat.
test/tutorial007/Providers.idrnew file+27
@@ -0,0 +1,27 @@
1+module Providers
2+
3+%access export
4+
5+%dynamic "./nativetypes.so"
6+
7+sizeOfSizeT : IO Int
8+sizeOfSizeT = foreign FFI_C "sizeof_size_t" (IO Int)
9+
10+public export
11+data NativeTypeSize = OneByte | TwoBytes | FourBytes | EightBytes
12+
13+Show NativeTypeSize where
14+ show OneByte = "1 byte"
15+ show TwoBytes = "2 bytes"
16+ show FourBytes = "4 bytes"
17+ show EightBytes = "8 bytes"
18+
19+bytesToType : Int -> Provider NativeTypeSize
20+bytesToType 1 = Provide OneByte
21+bytesToType 2 = Provide TwoBytes
22+bytesToType 4 = Provide FourBytes
23+bytesToType 8 = Provide EightBytes
24+bytesToType _ = Error "Unsupported size"
25+
26+getSizeOfSizeT : IO (Provider NativeTypeSize)
27+getSizeOfSizeT = map bytesToType sizeOfSizeT
test/tutorial007/expectednew file+1
@@ -0,0 +1 @@
1+Pass
test/tutorial007/nativetypes.cnew file+13
@@ -0,0 +1,13 @@
1+#include <stdio.h>
2+
3+int sizeof_size_t() {
4+ int sz = sizeof(size_t);
5+
6+ FILE *f = fopen("sizefromc.txt", "w");
7+ if (f == NULL) {
8+ printf("Failed to open file from C");
9+ }
10+ fprintf(f, "%d bytes", sz);
11+
12+ return sz;
13+}
test/tutorial007/runnew file+5
@@ -0,0 +1,5 @@
1+#!/usr/bin/env bash
2+gcc -shared -fPIC nativetypes.c -o nativetypes.so
3+idris $@ tutorial007.idr -o tutorial007
4+./tutorial007
5+rm -f tutorial007 *.ibc *.so sizefromc.txt
test/tutorial007/tutorial007.idrnew file+12
@@ -0,0 +1,12 @@
1+module Main
2+import Providers
3+
4+%language TypeProviders
5+%provide (szSizeT : NativeTypeSize) with getSizeOfSizeT
6+
7+main : IO ()
8+main = do
9+ (Right expected) <- readFile "sizefromc.txt" | (Left err) => printLn err
10+ putStrLn $ if show szSizeT == expected
11+ then "Pass"
12+ else "Fail: \"" ++ show szSizeT ++ "\" /= \"" ++ expected ++ "\""