# Platform Specifics

`libplatform.h`/`libplatform.cpp` collect the platform-conditional (`#ifdef SIMWINDOWS`) pieces the rest of the codebase needs but shouldn't have to special-case itself: aligned allocation, floating-point classification safe for non-floating-point template arguments, the system `PATH`, and locating the Python and OpenVAF executables. `exportdef.h` and `dynload.h` round out "platform specifics": DLL symbol-visibility macros and dynamic library loading, both covered at the end of this page.

## The system path

`pathSeparator()` returns `;` on Windows, `:` elsewhere - the OS's `PATH`-variable list separator. `splitString()` is a small general-purpose delimiter split (skipping empty fields) used to break the `PATH` environment variable into a list, which `LibPlatform::systemPath()` caches once via a function-local static. This is the list [File Access](filesystem.md)'s `findFileInSystemPath()` and [Process Utilities](processutils.md)'s `findProgram()` search.

## Aligned allocation

`alignedAlloc()`/`alignedFree()` portably wrap `std::aligned_alloc()`/`free()` (POSIX) or `_aligned_malloc()`/`_aligned_free()` (MSVC, whose standard library doesn't implement the C++17 aligned-allocation functions). The POSIX branch additionally rounds `size` up to a multiple of `alignment` before calling `std::aligned_alloc()`, since that function requires the size to already be a multiple of the alignment as a precondition - callers of `alignedAlloc()` don't need to remember this themselves.

## Floating-point classification for any type

`isInf<T>()`/`isNaN<T>()`/`isFinite<T>()` wrap `std::isinf`/`std::isnan`/`std::isfinite`, guarded by `if constexpr(std::is_floating_point<T>::value)` so they can be instantiated for any `T` - not just `float`/`double` - returning `false` for a non-floating-point `T` instead of failing to compile. This matters because MSVC's standard library rejects calling those functions on non-floating-point arguments, unlike GCC/Clang; it lets generic code (e.g. [functors](rpnfunctor.md)'s `FwIsInf`/`FwIsNan`/`FwIsFinite`, instantiated over both `Int` and `Real`) call one of these uniformly instead of branching on the argument type itself at every call site.

## Locating external executables

`findPythonExecutable()` and `defaultOpenVafBinaryName()` locate the `python3`/`python` (or `python.exe`) and `openvaf-r`(`.exe`) programs the simulator shells out to, for postprocessing scripts and on-the-fly Verilog-A compilation respectively - both platform-name-aware, and the Python lookup goes through `findFileInSystemPath()` ([File Access](filesystem.md)).

## `formattedTimestamp()`

Returns the current local time as a human-readable string (`"%a %b %e %H:%M:%S %Y"`, e.g. `Thu Aug 13 10:15:03 2026`) - used to stamp the `Date:` header of a rawfile output (`outrawfile.cpp`). The implementation differs by platform because of library support, not behavior: everywhere except macOS it uses `<chrono>`'s `std::chrono::zoned_time` and `std::format` directly; on macOS (where those were not yet available in the targeted standard library) it falls back to `localtime_r()` plus `std::put_time()` into an `ostringstream`, formatted the same way.

## DLL export macros (`exportdef.h`)

`DLL_PUBLIC`/`DLL_LOCAL` mark the simulator library's public API when it is built or consumed as a shared library, so the same headers compile correctly whether producing a DLL/shared object (`BUILDING_SIM_DLL`), linking against one (`USING_SIM_DLL`), or neither (a plain static build).

On Windows/Cygwin, `DLL_PUBLIC` maps to `dllexport` when building the DLL and `dllimport` when consuming it - via GCC's `__attribute__((dllexport/dllimport))` syntax or MSVC's `__declspec`, selected by which compiler is active. `DLL_LOCAL` is defined empty on this branch: Windows import/export libraries have no equivalent to ELF's "hidden but still linkable within the library" visibility, so there's nothing meaningful to mark internal symbols with there.

On other platforms with GCC 4 or newer, and only when actually building or using the DLL, `DLL_PUBLIC`/`DLL_LOCAL` map to ELF visibility attributes (`default`/`hidden`) instead. In every other case - a static build, or an old GCC without visibility support - both macros expand to nothing, since visibility control has no effect (and no cost to leaving unset) outside a shared-library build.

## Dynamic library loading (`dynload.h`)

`openDynamicLibrary()`/`closeDynamicLibrary()`/`dynamicLibrarySymbol()`/`dynamicLibraryError()` are a thin, four-function portable wrapper over the OS's dynamic-loading API: `LoadLibrary`/`FreeLibrary`/`GetProcAddress` on Windows, `dlopen`/`dlclose`/`dlsym` elsewhere, all working in `void*` handles/symbols rather than the platform-specific handle types so callers don't need `#ifdef SIMWINDOWS` of their own. `dynamicLibraryError()` retrieves the last error as a string on either platform - on Windows this means formatting the numeric `GetLastError()` code via `FormatMessage` into readable text (freeing the buffer `FormatMessage` allocates afterward), where POSIX's `dlerror()` already returns a message string directly.

This is what loads an OSDI device module (a compiled Verilog-A shared library) at runtime - see `lib/osdifile.cpp`, the sole caller - keeping the OS-specific loading calls out of the device-loading logic itself.
