cross-platform-guide

Use when making native C/C++ software portable across operating systems and targets: isolating all OS/windowing/input/audio calls behind one platform-abstraction interface, ordering a port to a new OS

SKILL.md

GitHub

Cross-platform / portability Guidelines

How to make native C/C++ software portable across operating systems and targets by concentrating every OS dependency behind a thin abstraction, porting one backend at a time, and reaching new targets including the web and new input hardware. The renderer architecture itself lives in gpu-rendering-guide (and gpu-rendering-vulkan-guide for one API); build-system mechanics live in cmake-guide; the C dialect's per-platform file-naming in c99-opinionated-guide.

Requirements

  • A codebase where OS access can be (or already is) funneled through interfaces, not called inline throughout app/engine code.
  • A build system that can select one backend translation unit per target and define per-platform macros.

Essentials

  • One interface for all OS calls - A function-pointer struct (or build-selected header) per concern; callers never #ifdef or call an OS API directly, see references/platform-abstraction-layer.md
  • Stub, then fill, in interactivity order - Window → input → OS services → audio; the program links against stubs from hour one, see references/porting-strategy.md
  • The web is a backend with hard constraints - Cooperative main loop, async I/O, GL ES mapping, 32-bit pointers, see references/web-wasm-builds.md
  • Devices are a backend too - Enumerate, probe, poll non-blocking, map by code, behind the input-source interface, see references/input-devices.md

Abstraction and Porting

Web / WebAssembly

Input Devices

Gotchas

  • A backend that leaks a raw OS handle (file descriptor, window handle, connection) re-couples callers to one platform: keep the type opaque at the interface boundary.
  • Reverse-engineering the abstraction from one platform bakes that platform's assumptions in (one global clipboard, OS-reported relative mouse motion); design the interface before the first backend.
  • A stricter target compiler surfacing new warnings almost always means the warnings were always real: fix void f() → void f(void), unused vars, and declaration mismatches rather than suppressing.
  • 32-bit pointers (Wasm) silently change struct padding, breaking any hash/memcmp keyed on the byte layout; widen keys and re-pad for that build.
  • A blocking while (running) loop hangs the browser tab; the browser owns the frame clock, so the frame must be a callback that returns each iteration.
  • fopen that "works natively" returns nothing in the browser unless the file was preloaded into the virtual filesystem or fetched asynchronously first.
  • A device node under /dev/input is not necessarily the device you want: a keyboard and a gamepad both appear there; verify capability bits before mapping.
  • Raw eventN numbers and per-device axis ranges are not stable or symmetric; bind off the by-id symlink and normalize using the queried min/max and flat deadzone.

Progressive Disclosure

  • Read references/platform-abstraction-layer.md - Load when designing or refactoring the OS-abstraction interface, removing scattered #ifdefs, or adding a per-platform backend
  • Read references/porting-strategy.md - Load when bringing the software up on a new OS: ordering the work, stubbing, handling compiler/toolchain and pointer-width differences, CI matrix
  • Read references/web-wasm-builds.md - Load when compiling to the web via Emscripten/WebAssembly: main loop, async I/O, GL ES/WebGPU mapping, memory growth, build flags, static linking
  • Read references/input-devices.md - Load when enumerating/reading input devices (gamepads via evdev), handling hotplug, or mapping buttons/axes behind the input interface