Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/alexf91/lean4-ctypes
FFI for Lean 4
https://github.com/alexf91/lean4-ctypes
ffi lean lean4
Last synced: about 1 month ago
JSON representation
FFI for Lean 4
- Host: GitHub
- URL: https://github.com/alexf91/lean4-ctypes
- Owner: alexf91
- License: apache-2.0
- Created: 2023-09-05T22:19:39.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2023-12-27T03:59:13.000Z (11 months ago)
- Last Synced: 2023-12-27T04:57:48.466Z (11 months ago)
- Topics: ffi, lean, lean4
- Language: C++
- Homepage:
- Size: 392 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- License: COPYING
Awesome Lists containing this project
README
# CTypes for Lean 4
CTypes is a foreign function library for Lean 4, inspired by Python's [ctypes](https://docs.python.org/3/library/ctypes.html) module.
It provides C compatible data types, and allows calling functions in shared libraries. It can be used to wrap these libraries in pure Lean 4.## Usage
Library functions are accessed through the `Library` type.
It is a wrapper around `dlopen()` for loading a shared library and `dlsym()` for looking up a symbol.The `CType` type represents types available in C.
It contains definitions for signed and unsigned integers, floating point types, complex types, structs and pointers.Values are represented with the `CValue` type.
It directly matches the `CType` type, but also contains a value.The low-level implementation is in the `CTypes.Core` namespace and implements basic types and function calls.
It is a wrapper around `libffi`.### Basic concepts
This example calls the function `pow()` in the library `libm.so.6`:
```Lean
import CTypes
open CTypes.Coredef main (_ : List String) : IO UInt32 := do
-- Open the library. See man page for dlopen() for flags.
let lib ← Library.mk "libm.so.6" .RTLD_NOW #[]-- Lookup the symbol `pow`.
-- As an alternative `libm["pow"]` can be used.
let pow ← lib.symbol "pow"-- Call the function with two regular `CValue` objects and no variadic arguments.
let result ← pow.call .double #[.double 1.4142, .double 2.0] #[]-- The result has type `CType.double`.
IO.println s!"result: {repr result}"return 0
```### Pointers
While equivalents to basic C types exist in Lean, this is not the case for pointers.
The `Pointer` type is used to access raw memory from Lean.
They support pointer arithmetic and dereferencing.Unless noted otherwise, the library will never allocate or free memory on its own.
This has to be done by the user with `malloc()`, `free()` and similar functions.```Lean
import CTypes
open CTypes.Coredef main (_ : List String) : IO UInt32 := do
let lib ← Library.mk "libc.so.6" .RTLD_NOW #[]
let malloc ← lib["malloc"]
let free ← lib["free"]-- Allocate a `int16_t` buffer.
let p ← malloc.call .pointer #[.size_t CType.int16.size] #[]
-- Write the value.
p.pointer!.write (.int16 42)
-- Read back the value.
let value ← p.pointer!.read .int16IO.println s!"value: {repr value}"
-- Free the allocated buffer.
discard <| free.call .void #[p] #[]return 0
```### Callbacks
Lean functions can be called from C by creating a `Closure` object.
Pointers to the closure can be passed to C functions or called like regular function pointers.```Lean
import CTypes
open CTypes.Coredef main (_ : List String) : IO UInt32 := do
-- Callback functions have the signature (Array CValue) → IO CValue
let add : Callback := fun args => do
return .int (args[0]!.int! + args[1]!.int!)-- Create the closure.
let closure ← Closure.mk .int #[.int, .int] add-- Call the function through the `Pointer.call` interface.
let result ← closure.pointer.call .int #[.int 42, .int 11] #[]
IO.println s!"42 + 11 = {result.int!}"-- Closures are not deleted by default, in case a C function stores a reference.
-- They have to be marked for deletion by the user.
-- Deleted closures are freed when the object is garbage collected. Otherwise they
-- leak memory.
closure.deletereturn 0
```### Structs and arrays
Structs are described as an array of types and instantiated as an array of values.
There is no direct support for arrays in the `CTypes.Core` library.
If arrays should be passed to functions, then the buffer has to be allocated with `malloc()`.
If an array is a member of a struct, then the array can created with a struct with one value for each array element.Arrays created this way can not be passed to functions.
Here is an example that uses callbacks and a pointer to a struct to calculate the Fibonacci sequence:
```Lean
import CTypes
open CTypes.Coredef main (_ : List String) : IO UInt32 := do
-- Fibonacci iteration with a struct as state variable.
let fib : Callback := fun args => do
let state ← args[0]!.pointer!.read $ .struct #[.int, .int]-- Get the two values from the (int, int) struct.
let a := state.struct![0]!.int!
let b := state.struct![1]!.int!-- Update the values and return the result.
args[0]!.pointer!.write (.struct #[.int b, .int (a + b)])
return .int a-- The closure object for the C function.
let closure ← Closure.mk .int #[.pointer] fib-- Initialize the state struct to (0, 1).
let libc ← Library.mk "libc.so.6" .RTLD_NOW #[]
let state ← (← libc["malloc"]).call .pointer #[.size_t (CType.struct #[.int, .int]).size] #[]
state.pointer!.write $ .struct #[.int 0, .int 1]for _ in [0:8] do
let result ← closure.pointer.call .int #[state] #[]
IO.println $ repr result-- Cleanup
closure.delete
discard <| (← libc["free"]).call .void #[state] #[]return 0
```## Build instructions
Building the library is still experimental and requires that the same compiler is used for code generated by the Lean compiler and the C++ files in `src/`.
This requires setting `LEAN_CC` when `lake` is called: `LEAN_CC=clang++ lake build`.
Only Linux systems and the `clang++` compiler are currently supported.Tests can be executed with `LEAN_CC=clang++ lake exe tests`.