Claude-skill-registry ffi-bindings
Create FFI bindings between Lean 4 and C code. Use when working with foreign functions, native libraries, Metal, or system APIs.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/ffi-bindings" ~/.claude/skills/majiayu000-claude-skill-registry-ffi-bindings && rm -rf "$T"
manifest:
skills/data/ffi-bindings/SKILL.mdsource content
FFI Bindings
Create Foreign Function Interface bindings between Lean 4 and C.
Quick Start
- Define opaque types for native handles
- Declare extern functions in Lean
- Implement C functions with proper memory management
- Register external classes for garbage collection
- Update build.sh for compilation
Lean Side: Opaque Types
-- Define an opaque type backed by a native pointer opaque WindowPointed : NonemptyType def Window : Type := WindowPointed.type instance : Nonempty Window := WindowPointed.property
Lean Side: Extern Functions
-- Simple function @[extern "lean_window_create"] opaque Window.create : UInt32 → UInt32 → String → IO Window -- Function returning a value @[extern "lean_window_get_size"] opaque Window.getSize : Window → IO (UInt32 × UInt32) -- Function with no return value @[extern "lean_window_close"] opaque Window.close : Window → IO Unit -- Pure function (no IO) @[extern "lean_add_vectors"] opaque Vec3.add : Vec3 → Vec3 → Vec3
C Side: External Class Registration
#include <lean/lean.h> // Global class pointer (initialized once) static lean_external_class* g_window_class = NULL; // Destructor called when Lean GC collects the object static void window_finalizer(void* ptr) { Window* window = (Window*)ptr; window_destroy(window); } // Initialize the class (call once at startup) static void ensure_class_initialized() { if (g_window_class == NULL) { g_window_class = lean_register_external_class( window_finalizer, // destructor NULL // foreach (for nested lean objects) ); } }
C Side: Creating Objects
LEAN_EXPORT lean_obj_res lean_window_create( uint32_t width, uint32_t height, lean_obj_arg title_obj, lean_obj_arg world ) { ensure_class_initialized(); // Extract string from Lean const char* title = lean_string_cstr(title_obj); // Create native object Window* window = window_new(width, height, title); // Wrap in Lean external object lean_object* result = lean_alloc_external(g_window_class, window); return lean_io_result_mk_ok(result); }
C Side: Extracting Native Pointers
LEAN_EXPORT lean_obj_res lean_window_close( lean_obj_arg window_obj, lean_obj_arg world ) { // Extract native pointer Window* window = (Window*)lean_get_external_data(window_obj); window_close(window); return lean_io_result_mk_ok(lean_box(0)); }
C Side: Returning Tuples
LEAN_EXPORT lean_obj_res lean_window_get_size( lean_obj_arg window_obj, lean_obj_arg world ) { Window* window = (Window*)lean_get_external_data(window_obj); uint32_t w = window_get_width(window); uint32_t h = window_get_height(window); // Create tuple (UInt32 × UInt32) lean_object* pair = lean_alloc_ctor(0, 2, 0); lean_ctor_set(pair, 0, lean_box_uint32(w)); lean_ctor_set(pair, 1, lean_box_uint32(h)); return lean_io_result_mk_ok(pair); }
C Side: Working with Floats
// Box a float for return lean_object* boxed = lean_box_float(value); // Unbox a float from argument double value = lean_unbox_float(float_obj); // Return Float × Float tuple lean_object* pair = lean_alloc_ctor(0, 2, 0); lean_ctor_set(pair, 0, lean_box_float(x)); lean_ctor_set(pair, 1, lean_box_float(y));
C Side: Working with Arrays
// Create a Lean array lean_object* arr = lean_mk_empty_array(); for (int i = 0; i < count; i++) { arr = lean_array_push(arr, lean_box_uint32(values[i])); } // Read from Lean array size_t len = lean_array_size(arr); for (size_t i = 0; i < len; i++) { lean_object* elem = lean_array_get_core(arr, i); uint32_t val = lean_unbox_uint32(elem); }
build.sh Template
#!/bin/bash set -e # Set compiler for FFI export LEAN_CC=/usr/bin/clang # For macOS frameworks export LIBRARY_PATH="/opt/homebrew/lib:$LIBRARY_PATH" # Build with Lake lake build # Or for Metal projects on macOS # clang -c -o ffi.o ffi.c $(pkg-config --cflags lean4) # clang -shared -o libffi.dylib ffi.o -framework Metal -framework Foundation
Common Patterns
Optional Values
// Return none return lean_io_result_mk_ok(lean_mk_option_none()); // Return some value return lean_io_result_mk_ok(lean_mk_option_some(value));
Error Handling
// Return IO error if (error) { lean_object* err = lean_mk_io_user_error( lean_mk_string("Error message") ); return lean_io_result_mk_error(err); }
Projects Using FFI
Reference implementations:
- Metal GPU renderinggraphics/afferent
- Terminal I/Ographics/terminus
- SQLite bindingsdata/quarry
- AudioToolboxaudio/fugue
- gRPCnetwork/legate