Skip to content
nim-c-wrappers logo

Nim C Wrappers

nim-c-wrappers

Turn low-level C FFI bindings into an idiomatic Nim API, with safer types, cleaner module boundaries, resource management, callbacks, enums, bitflags, and exception-based error handling. Use when building a high-level Nim wrapper around a C library or improving the usability of existing raw bindi...

planetis-m/skills0installs59stars

SKILL.md

Full skill instructions

Nim C Wrappers

This skill covers the two-layer pattern for wrapping C libraries in Nim: a raw FFI layer for ABI correctness and an ergonomic Nim layer for safety and usability. Larger examples live in references/.

Rules

Architecture

  • Use a two-layer design: raw FFI layer (ABI-faithful) + ergonomic Nim layer (safe, idiomatic). Keep the raw layer thin and stable.
  • Split modules by library domain. Mirror the C header structure for large libraries.
  • Centralize shared types in a lib_raw_types module to avoid cyclic imports.
  • Export raw symbols only for advanced users; re-export selected symbols from ergonomic modules.

Raw FFI Layer

  • Use importc with cdecl (or stdcall if the library requires it). Prefer {.push callconv: cdecl, header: "foo.h".} blocks for shared conventions.
  • Use {.header.} for static linking, {.dynlib.} for dynamic linking. Guard library names with when defined(windows): etc.
  • Never reorder struct fields. Use object in C field order. Add packed only if C headers specify packing.
  • Use incompleteStruct and list only needed fields to reduce ABI risk.
  • For C enums, use distinct integer types + const values. Do not use Nim enum in raw bindings. Find actual enum values in C headers—do not guess.
  • For bitflags, use distinct integer types with bitwise helpers. Do not use set[Enum].
  • Map C macros: numeric → const; function-like → inline proc or template; sizeof/​side-effect → template.
  • Keep pointer types as C intends — do not convert them in the raw layer.

Type Mapping

C TypeNim TypeNotes
charccharPlatform-dependent signedness
signed charcscharAlways int8
unsigned charuint8Safer default spelling; avoid relying on cuchar status across Nim versions
shortcshortAlways int16
unsigned shortcushortAlways uint16
intcintAlways int32
unsigned intcuintAlways uint32
longclongABI-sized (4 on Win64, 8 on LP64)
unsigned longculongABI-sized
long longclonglongAlways int64
unsigned long longculonglongAlways uint64
size_tcsize_tAlias for uint
intptr_tintPointer-sized signed
uintptr_tuintPointer-sized unsigned
floatcfloatAlways float32
doublecdoubleAlways float64
long doubleclongdoubleLimited codegen support
void*pointerUntyped
T*ptr TNullable
T**ptr ptr T
char* / const char*cstringNUL-terminated
char**cstringArrayptr UncheckedArray[cstring]

Struct types: object in C order. Fixed arrays: array[N, T]. Pointer+length: ptr T + csize_t. Raw buffers: ptr UncheckedArray[T]. Opaque handles: pointer or ptr OpaqueObj (empty object).

Ergonomic Layer

  • For move-only resources: implement =destroy, =wasMoved, =sink; mark =copy and =dup with {.error.}. Use ensureMove() for ownership transfer.
  • For reference-counted resources: use a ptr int counter. =copy/=dup increment, =destroy decrements and frees at zero. Use field-by-field assignment in =dup, not result = src.
  • In =destroy, explicitly call =destroy on owned nested GC-managed fields (string, seq) after releasing C resources.
  • Define ownership hooks in the same module as the type. Cross-module hook definitions are a compile error.
  • Raise exceptions (IOError, ValueError, etc.) for C errors — do not return result wrappers that only carry ok/​kind/​message.
  • Do not create custom exception types unless callers handle them differently.
  • Catch errors only at translation boundaries (C return code → Nim exception, or exception → domain result). Let exceptions propagate otherwise.

Naming

  • Strip redundant C prefixes (LIB_, foo_); keep names that disambiguate or match docs.
  • Keep raw constant names in C style (e.g., CURLE_OK).
  • Rename Nim keywords: type → typ, addr → address, or use importc: to preserve the C name.

Callbacks

  • Declare callbacks as plain C-callable procs such as proc onEvent(code: cint; userData: pointer) {.cdecl.}. Do not pass Nim closures to C.
  • For callback state, use a global table keyed by userdata. Ensure Nim data is globally rooted or manually managed.

Verification

  • Add static: doAssert sizeof(T) == N and offsetOf checks in tests to verify struct layouts.
  • Test ABI with compile + link + smoke test + runtime checks.

Workflow

  1. Read the C API. Identify structs, enums, functions, callbacks, and ownership semantics.
  2. Map types. Use the type table. Verify sizes with static: doAssert sizeof.
  3. Write raw bindings. One module per C header or subsystem. Centralize shared raw types to avoid cycles. Use importc, correct calling convention, packed only when C specifies it. Use typed integer aliases for enums.
  4. Write ergonomic wrappers. Add destructors for owned resources (move-only or RC). Raise exceptions for C errors. Re-export only the intended public surface. Strip prefixes.
  5. Test. Compile check → link check → smoke test → ABI sizeof/​offset asserts → runtime ownership tests.
  6. Iterate. Add missing APIs as needed. Keep raw layer stable.

Common Mistakes

MistakeWhy it's wrong
Passing Nim string directly to char* parameterType mismatch; use .cstring
Using seq where C expects stable pointeradd() can reallocate, invalidating the pointer
Reordering struct fieldsBreaks ABI layout, causes garbage values
Using Nim enum for C enum valuesABI incompatibility; use typed integer aliases
Adding enum or constant values without inspecting headersFind actual enum values in C headers, do not guess
Passing closures as C callbacksC expects plain function pointers, not GC closures
Storing cstring beyond the callcstring from string.cstring dangles after the source is freed
=dup body using result = src in RC patternMay trigger implicit =copy instead of =dup; use field-by-field assignment
Wrong calling conventionCauses crashes at call sites

References

  • references/​move_only_resource.md — Complete move-only resource wrapper with destructor hooks
  • references/​rc_resource.md — Reference-counted (shared ownership) resource wrapper
  • references/​callback_pattern.md — C callback pattern using direct pointer casting
  • references/​enum_and_bitflags.md — Typed aliases for enums and distinct types for bitflags
  • references/​module_layout.md — Shared-types module plus selective ergonomic re-exports

Changelog

  • 2026-04-09: Initial verified skill. Added dynlib loading, nested destruction, callback registries, multi-module layout.

More skills from planetis-m

tts-tool logo
planetis-m/tts-tool

tts-tool

Generate a natural-sounding `.opus` audio file from text or markdown. Use when a user wants spoken audio, especially for content with markdown, URLs, formulas, paths, or technical notation that should be rewritten for speech.

3 0
View
nim-api-design logo
planetis-m/skills

nim-api-design

Design clear public Nim APIs for libraries and modules, including exported types, constructors, lookup functions, error contracts, and container-style interfaces. Use when creating or reviewing a Nim library API, designing an exported module surface, or deciding how callers should construct, acce...

59 0
View
ocr-tool logo
planetis-m/ocr-tool

ocr-tool

Extract raw text from PDF files with OCR, including full-document extraction, selected page ranges, and reuse of cached pages. Use when a user asks to OCR a PDF, extract text from a scanned PDF, or read selected PDF pages.

1 0
View
nim-style-guide logo
planetis-m/skills

nim-style-guide

Write clear, consistent Nim code in a simple stdlib-aligned style, covering imports, naming, proc vs func vs template choices, local variables, constructors, formatting, and control flow. Use when writing new Nim code or reviewing a Nim module for readability, consistency, and low-noise style dec...

59 0
View
rag-tool logo
planetis-m/rag-tool

rag-tool

Store and search text or markdown as semantic chunks in a reusable local vector store. Use when a user wants to ingest documents or retrieve relevant passages from previously stored material.

1 0
View
study-assistant logo
planetis-m/study-assistant

study-assistant

Study assistant workflow for turning provided study material into exam-focused deliverables such as verbatim transcriptions, study notes, lecture-style explanations, ELI5 explanations, flashcards, Mermaid mind maps, quizzes, and essay questions.

5 0
View
nim-code-organization logo
planetis-m/skills

nim-code-organization

Organize Nim code into clear modules and multi-step workflows, with explicit state, top-level helper procs, narrow exports, and easy-to-follow orchestration. Use when refactoring a large Nim file, splitting logic across modules, designing parser-style stateful code, or cleaning up nested helpers ...

59 0
View
nim-ownership-hooks logo
planetis-m/skills

nim-ownership-hooks

Implement and review Nim ARC/ORC ownership hooks for types that manually manage resources, including move-only, deep-copy, refcounted, and copy-on-write patterns. Use when a Nim type owns pointers, buffers, file descriptors, or custom heap memory and you need correct `=destroy`, `=copy`, `=dup`, ...

59 0
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View