This commit is contained in:
Qwreey 2024-10-24 06:03:03 +00:00 committed by GitHub
commit 53336c614b
Signed by: DevComp
GPG key ID: B5690EEEBB952194
2 changed files with 117 additions and 1 deletions

View file

@ -0,0 +1,115 @@
import { FileTree, Tabs, Tab, Callout } from 'nextra/components'
# FFI
Luna has built-in library for handling foreign function, [`ffi`](../../api-reference/ffi.md).
This library will let you call external function, allocate memories with specific size and value, create closures, and more.
## Example File Tree
Let's use this directly & file tree sturcture for our examples:
<FileTree>
<FileTree.Folder name="-" defaultOpen>
<FileTree.File name="call.luau" />
<FileTree.File name="closure.luau" />
<FileTree.Folder name="external" defaultOpen>
<FileTree.File name="lib.c" />
<FileTree.File name="lib.so" />
</FileTree.Folder>
</FileTree.Folder>
</FileTree>
<details>
<summary>Show file contents</summary>
```c copy filename="external/lib.c"
int addNumber(int a, int *b) {
return a + *b;
}
typedef int(*closure_t)(int, int*);
int callClosure(closure_t closure) {
int b = 200;
return closure(100, &b) * 2;
}
```
</details>
<Callout type="info" emoji="❔">
To create the `external/lib.so` file, you will need a C compiler like `gcc`. Run c compiler with the following command: `gcc -shared -o external/lib.so -fPIC external/lib.c`.
</Callout>
## Call external function
```lua copy filename="call.luau"
local ffi = require("@lune/ffi")
local c = ffi.c
--> Open dynamic library
local lib = ffi.open("./external/lib.so")
--> Create function signature
local addNumberInfo = c.fn({ c.int, c.int:ptr() }, c.int)
--> Get symbol from library and create callable
local addNumber = addNumberInfo:callable(lib:find("addNumber"))
--> Create memory for result
local resultBox = ffi.box(c.int.size)
--> Create arguments
local aBox = c.int:box(100)
local bBox = c.int:box(200)
--> Call external function. all arguments should be references.
--> If you want to pass a pointer as argument, call `:ref()` again.
addNumber(resultBox, aBox:ref(), bBox:ref():ref())
--> Read number from resultBox
local result = c.int:readData(resultBox)
print(result) -- 300
```
Note that All data is automatically freed by the garbage collector. If external function stores pointer in somewhere or frees pointer, you should call `:leak()` to leak it.
## Create closure from lua function
```lua copy filename="closure.luau"
local ffi = require("@lune/ffi")
local c = ffi.c
--> Open dynamic library
local lib = ffi.open("./external/lib.so")
--> Create closure function signature
local closureInfo = c.fn({ c.int, c.int:ptr() }, c.int)
--> Create closure with lua function
local closure = closureInfo:closure(function(resultRef, aRef, bRefRef)
--> Convert arguments to lua number
local a = c.int:readData(aRef)
local b = c.int:readData(bRefRef:deref())
--> Write a+b into result reference
c.int:writeData(resultRef, a + b)
print("Closure returned: " .. (a + b))
end)
--> Create callClosure function signature
local callClosureInfo = c.fn({ closureInfo }, c.int)
--> Get symbol from library and create callable
local callClosure = callClosureInfo:callable(lib:find("callClosure"))
--> Create memory for result
local resultBox = ffi.box(c.int.size)
--> Call external function.
callClosure(resultBox, closure:ref())
--> Read number from resultBox
local result = c.int:readData(resultBox)
print(result) -- 600
```

View file

@ -8,5 +8,6 @@
"7-environment-variables": "7 • Environment Variables",
"8-modules": "8 • Modules",
"9-task-scheduler": "9 • Task Scheduler",
"10-spawning-processes": "10 • Spawning Processes"
"10-spawning-processes": "10 • Spawning Processes",
"11-ffi": "11 • Foreign Function Interface"
}