Opening a Dynamic Library
Guide by FenRave. Message for edits, remarks or questionsInclusive of .so/.dll/.dylib types of dynamic libraries.
0.5.*How to open a library
Assume we have an API written in C, such as:
Library APIextern void PassNumbers(int num, int othernum);
extern int GetNumber(void);Lets also assume this will compile to a dynamic libary, for this example, lets assume “SharedExample.so” for the sake of this example.
Knowing this, we can now open the library like so:
Opening the Library in Zunelocal ffi = zune.ffi
local void = ffi.types.void
local int = ffi.types.i32
--[=========================[+
This function is commonly used in zune bindings since it saves on writing out the whole table,
otherwise you'd have to do {return = {returntypes}, args = {argtypes}} everytime.
+]=========================]--
local function fn(returns: any, args: { any })
return {
returns = returns,
args = args,
}
end
export type API = {
PassNumbers: (num: number, othernum: number) -> (),
GetNumber: () -> number,
}
local API_Example = {
PassNumbers = fn(void, {int, int}),
GetNumber = fn(int, {}) --you don't include 'void' as an arg type.
}
--assume we have a top src directory
local path = "src/bin/SharedExample.so"
local lib = ffi.dlopen(path, API_Example) :: API
print(lib.GetNumber()) --returns a numberCompiling a Library within Zune
Zune also bundles a C compiler within its FFI library, allowing you to compile C code at runtime into a library.
local ffi = zune.ffi
local int = ffi.types.i32
local API = ffi.c.compile([[
int sub(int num1, int num2) {
return num1 - num2;
}
]])
local sub = ffi.fn(
--Reusing the function provided earlier
fn(int, {int, int}),
API:getSymbol("sub")
)
print(sub(20, 10)) --10The bundled compiler is a version of TinyCC, which does not provide all C language extensions, unlike GCC/MSVC/Clang which may provide most of them, but it does provide many of the important ones.
Inline Assembly
One extension provided in TinyCC, though, is GCC inline assembly.
Because TinyCC doesn’t necessarily aim to produce highly optimized code, as its goals differ from that of the aforementioned compilers, you can take matters into your own hand and write your own assembly functions:
local ffi = zune.ffi
local int = ffi.types.i32
local API = ffi.c.compile([[
extern inline int sub(int num1, int num2) {
int ret;
asm (
"sub %[input1], %[input2]\n\t"
"mov %[input2], %0"
: "=r"(ret) : [input1] "r"(num1), [input2] "r"(num2)
);
// '=r' is the return operand, which is also represented by $0
// 'input1' can represent any register, but its usually something like eax or edi
// 'input2' likewise will also represent any register
return ret;
}
]])
local sub = ffi.fn(
fn(int, {int, int}),
API:getSymbol("sub")
)
print(sub(10, 20)) --10