# FFI: calling C

C+ emits standard object files. The system linker stitches them with anything `clang` would. The language-level interop primitive is `extern fn`.

## Declaring symbols

```cplus
extern fn malloc(n: usize) -> *u8;
extern fn free(p: *u8);
extern fn printf(fmt: *u8, ...) -> i32;   // varargs OK on extern only
```

## C-string literals: `c"..."`

C wants a NUL-terminated `char*`. A `c"..."` literal is exactly that: a bare `*u8` pointing at a NUL-terminated `.rodata` blob.

```cplus
extern fn printf(fmt: *u8, ...) -> i32;

fn main() -> i32 {
    unsafe { printf(c"hello, %d\n", 42 as i32); }   // c"..." is a *u8
    let banner: *u8 = c"=== ready ===\n";
    unsafe { printf(banner); }
    return 0;
}
```

A `c"..."` is a `*u8` (not the fat-pointer `str`) and is **safe to form**, since it is just a pointer to static data; only *dereferencing* a raw pointer needs `unsafe`. The NUL is appended for you. For an owned, length-carrying string use `string` or `str`; `c"..."` is specifically the C-interop shape.

## Raw pointers

`*T` is an 8-byte opaque address. It is `Copy`. **Every operation** on it requires `unsafe`:

```cplus
let p: *u8 = unsafe { malloc(64 as usize) };
unsafe {
    p[0] = 65 as u8;                  // store
    let b: u8 = p[1];                 // load
    let q: *u8 = p + 1;               // pointer arithmetic (strides by sizeof(T))
    free(p);
}
```

**Raw pointers are outside the borrow checker, by design.** The compiler tracks nothing about a `*T`'s lifetime: you can return one, store it in a global, or alias it freely. That is the escape hatch that makes FFI possible, and the flip side is that the validity obligation is entirely yours. A pointer into a value that has since dropped is a use-after-free the language will not catch. The `unsafe` you write at each dereference is exactly where you acknowledge taking on that obligation (see [Ownership](/docs/ownership), "what the compiler checks, and what it trusts").

Raw pointers also have a few blessed helper methods:

```cplus
if p.is_null() { return 1; }
if p.is_not_null() { unsafe { p.write_zeroed(); } }
```

`is_null()` / `is_not_null()` are safe bit-pattern checks. `write_zeroed()` is unsafe because it writes through the pointer.

## `unsafe { ... }`

Required for: pointer dereference, pointer indexing, `extern fn` calls, `str_from_raw_parts`, and integer-to-pointer casts.

The word `null` never appears. At an FFI boundary, a null pointer is written explicitly:

```cplus
let p: *u8 = unsafe { 0 as *u8 };
```

## `#[repr(C)]`: stable C layout

```cplus
#[repr(C)]
struct NSRect {
    origin: NSPoint,
    size: NSSize,
}
```

Promises field order is preserved and that padding and alignment match the platform C ABI. **Always** use it on structs that cross an `extern fn` boundary by value.

## `#[link_name = "..."]`: multiple signatures, one symbol

When one C symbol has several typed shapes (the Objective-C `objc_msgSend` pattern):

```cplus
#[link_name = "objc_msgSend"] extern fn msg_void(recv: *u8, sel: *u8);
#[link_name = "objc_msgSend"] extern fn msg_get_str(recv: *u8, sel: *u8) -> *u8;
```

Both resolve to `_objc_msgSend` at link time.

## Objective-C interop

Objective-C is the one non-C-shaped ABI that C+ treats as a first-class systems target, because AppKit, Foundation, Metal, and MPS all sit behind it on macOS. Object handles are opaque `*u8`, selectors are data, and message sends are unsafe calls. The direct compiler intrinsics are:

```cplus
let sel: *u8 = #selector("setTitle:");
unsafe {
    let title: *u8 = #msg_send(button, "title") -> *u8;
    #msg_send(button, "setEnabled:", true);
}
```

`#selector("name")` registers and caches the `SEL`. `#msg_send(recv, "sel", ...) -> T` emits a typed `objc_msgSend` call with the return type you spell at the call site. Most application code should import the typed packages instead: `vendor/appkit` wraps Cocoa and `vendor/metal` wraps Metal/MPS, keeping the unsafe details at the edge.

## Two ABI gotchas worth memorising

**Variadic functions must be declared variadic.** If the C header says `int fcntl(int fd, int cmd, ...);` the C+ extern must be variadic too. On AArch64-darwin, named args go in registers but varargs go on the stack, so a fixed-arity declaration silently passes garbage:

```cplus
extern fn fcntl(fd: i32, cmd: i32, ...) -> i32;       // ✅
extern fn fcntl(fd: i32, cmd: i32, arg: i32) -> i32;  // ❌ no-ops, returns 0
```

**Pointer/integer casts go through `usize`:**

```cplus
let n: usize = unsafe { p as usize };
let i: i32   = n as i32;
let bad: i32 = unsafe { p as i32 };   // ❌ E0315 — cannot cast pointer to i32
```
