C Bindings (bindgen)
The nicc bindgen command generates Nic FFI bindings from C header files, enabling seamless interop with C libraries.
Quick Start
bash
# Generate bindings to stdout
nicc bindgen mylib.h
# Generate to file
nicc bindgen mylib.h -o mylib.nic
# With include paths
nicc bindgen raylib.h -I/opt/raylib/include -o raylib.nicUsage
bash
nicc bindgen <HEADER> [OPTIONS]Options
| Option | Description |
|---|---|
-o FILE | Output file (stdout if not specified) |
-I PATH | Include path for clang (can be repeated) |
Examples
bash
# SQLite bindings
nicc bindgen /usr/include/sqlite3.h -o sqlite3.nic
# Multiple include paths
nicc bindgen complex.h -I./include -I/usr/local/include
# System library with custom paths
nicc bindgen raylib.h \
-I/opt/homebrew/include \
-o raylib.nicType Mappings
Primitive Types
| C Type | Nic Type |
|---|---|
void | unit |
char | char |
signed char, int8_t | i8 |
unsigned char, uint8_t | u8 |
short, int16_t | i16 |
unsigned short, uint16_t | u16 |
int, int32_t | i32 |
unsigned int, uint32_t | u32 |
long long, int64_t | i64 |
unsigned long long, uint64_t | u64 |
float | f32 |
double | f64 |
_Bool, bool | bool |
Pointer Types
| C Type | Nic Type |
|---|---|
T* | *T |
char* | string |
const char* | string |
void* | opaque |
T** | **T |
Complex Types
| C Type | Nic Type |
|---|---|
struct Foo { ... } | struct Foo { ... } |
enum Bar { ... } | enum Bar { ... } |
typedef T Alias | type Alias = T |
T arr[N] | [N]T |
Generated Output
Functions
C functions become Nic extern fn declarations:
c
// C header
int calculate(int x, int y);
void process(const char* name);nic
// Generated Nic
extern fn calculate(x: i32, y: i32) -> i32;
extern fn process(name: string) -> unit;Structs
C structs are converted to Nic structs:
c
// C header
typedef struct {
int x;
int y;
} Point;
struct Node {
int value;
struct Node* next;
};nic
// Generated Nic
pub struct Point {
x: i32,
y: i32
}
pub struct Node {
value: i32,
next: *Node
}Enums
C enums become Nic enums:
c
// C header
typedef enum {
RED = 0,
GREEN = 1,
BLUE = 2
} Color;nic
// Generated Nic
pub enum Color {
RED,
GREEN,
BLUE
}Type Aliases
c
// C header
typedef unsigned int uint;
typedef struct Point Point;nic
// Generated Nic
type uint = u32;
type Point = Point;Constants
Numeric #define macros are converted:
c
// C header
#define MAX_SIZE 1024
#define PI 3.14159nic
// Generated Nic
const MAX_SIZE: i32 = 1024;
const PI: f64 = 3.14159;Project Integration
Automatic Binding Generation
Add headers to your nic.toml for automatic generation:
toml
[ffi]
headers = ["vendor/sqlite3.h"]
include_paths = ["vendor/"]
libraries = ["sqlite3"]During build, bindings are generated to .nicc-build/ffi/:
.nicc-build/
└── ffi/
└── sqlite3.h.nicUsing Generated Bindings
Import the generated bindings in your code:
nic
// Import generated bindings
import "sqlite3.h"; // Imports from .nicc-build/ffi/sqlite3.h.nic
fn main() -> i32 {
let db: *opaque = nil as *opaque;
let rc = sqlite3_open(":memory:", &db);
// ...
return 0;
}Manual Workflow
For more control, generate bindings manually:
bash
# Generate bindings
nicc bindgen vendor/mylib.h -o src/bindings/mylib.nic
# Import in your code
# import bindings.mylib;Common Patterns
Opaque Pointers
C libraries often use opaque handles:
c
// C: opaque handle
typedef struct sqlite3 sqlite3;
int sqlite3_open(const char*, sqlite3**);nic
// Generated: opaque pointers
extern fn sqlite3_open(filename: string, ppDb: **opaque) -> i32;
// Usage
let db: *opaque = nil as *opaque;
sqlite3_open("test.db", &db);Callbacks
Function pointer parameters:
c
// C callback
typedef void (*Callback)(int status, void* data);
void register_callback(Callback cb, void* data);nic
// Generated
extern fn register_callback(cb: *fn(bare, i32, opaque)->unit, data: opaque) -> unit;
// Usage
fn my_callback(status: i32, data: opaque) -> unit {
printf("Status: %d\n", status);
}
register_callback(my_callback, nil as opaque);Variadic Functions
Variadic functions are partially supported:
c
// C
int printf(const char* format, ...);nic
// Generated - only fixed args, variadic part omitted
extern fn printf(format: string) -> i32;
// For full variadic support, use the function directly
extern fn printf(format: string, arg1: i32) -> i32; // Manual variantLimitations
- Macros: Only simple numeric
#defineconstants are converted - Inline functions: Not supported (compile as C and link)
- Bitfields: Not fully supported
- Complex macros: Function-like macros require manual wrapping
- Variadic functions: Only fixed arguments are bound
Workaround for Complex Cases
Create a C wrapper file for unsupported patterns:
c
// wrapper.c
#include "complex_lib.h"
// Wrap a macro as a function
int get_flag_value(void) {
return COMPLEX_MACRO_FLAG;
}
// Wrap inline function
int wrapped_inline(int x) {
return inline_function(x);
}toml
# nic.toml
[ffi]
sources = ["wrapper.c"]
headers = ["wrapper.h"] # Your simple wrapper headerTroubleshooting
Missing Headers
Error: fatal error: 'foo.h' file not foundAdd the include path:
bash
nicc bindgen mylib.h -I/path/to/headersType Not Found
If a type isn't recognized, it's mapped to opaque:
nic
// Unknown type becomes opaque
extern fn foo(handle: opaque) -> unit;Build Errors
If generated bindings don't compile, check for:
- Missing struct definitions (forward declarations)
- Circular type dependencies
- Platform-specific types
See Also
- Project Configuration - FFI section in nic.toml
- CLI Commands - bindgen command reference