abi-vtable-macro 0.3.1

Proc-macro generating #[repr(C)] C vtables (struct + thunks + static + getter) from Rust trait definitions, for the dyn-loader abi mode
Documentation
# abi-vtable-macro

`#[abi_vtable]` — annotate a Rust trait and get the complete C contract for
[dyn-loader](https://crates.io/crates/dyn-loader)'s **abi mode**:
a `#[repr(C)]` vtable struct, one `extern "C"` thunk per method, a static
vtable instance, a `#[no_mangle]` getter — and a host-side safe wrapper
(`CalcHost`) with one safe method per trait method — all generated.

```rust
use abi_vtable_macro::abi_vtable;

#[abi_vtable(name = "calc")]
pub trait Calc {
    fn add(&self, a: i32, b: i32) -> i32;
    fn is_even(&self, x: i32) -> bool;
}

struct MyCalc;
impl Calc for MyCalc {
    fn add(&self, a: i32, b: i32) -> i32 { a + b }
    fn is_even(&self, x: i32) -> bool { x % 2 == 0 }
}

// One line: static instance + thunks + vtable + getter.
abi_vtable_impl_calc!(MyCalc, MyCalc);
```

The host (Rust, C, C++, Zig, ...) loads `calc_get_vtable` and calls through
the struct of function pointers — see dyn-loader's `AbiTable`. A Rust host
that declares the same trait with the same `#[abi_vtable]` attribute also
gets the generated `CalcHost` wrapper and calls it safely:

```rust,ignore
let module = unsafe { AbiTable::<CalcVtable>::load(path, b"calc_get_vtable\0")? };
let host = CalcHost::new(module.vtable(), std::ptr::null_mut());
let sum = host.add(2, 3); // safe — no unsafe block needed
```

## Rules

- Field order of the generated vtable **is** the protocol: it follows trait
  method declaration order. Never reorder after release.
- All fn pointers are `extern "C"` (cdecl), ctx-leading (`*mut c_void` first).
- `$instance` must be const-constructible (unit struct, `const fn`, literal).

## License

MIT