# Companion language bindings (C++ and C#) wolfram's core SDK **must remain pure C23**. The C23 core (`libwolfram`) is the single source of truth: it owns transport, crypto, DAG-CBOR, DNS, and the server. Companion languages are **consumer layers** that link `libwolfram` and call into it — they must never re-implement signing, hashing, serialization, transport, or server logic. This document records the agreed conventions for the two C-adjacent bindings: **C++** (`wolfram-cpp`) and **C#** (`Wolfram.Interop`). ## Hard constraints (apply to every binding) 1. **Wrappers only — never reimplement.** The C++ and C# layers are thin pass-through wrappers over `libwolfram`. They must **not** reimplement any wolfram logic: no signing/hashing (libsecp256k1/OpenSSL), no DAG-CBOR or CAR (libcbor), no transport/DNS (libcurl/c-ares), no server (libmicrohttpd). Every wrapper ultimately calls a `libwolfram` function; the binding layer only adds ownership (RAII/`SafeHandle`), error mapping, and idiomatic naming. If wolfram has an honest stub (`WF_ERR_INVALID_ARG` + `TODO`), the binding surfaces that as an error/exception — never a fabricated success. 2. **Ownership mirrored 1:1.** wolfram exposes a uniform contract: every owned output type `wf_T` has a matching `wf_T_free`, and JSON strings use `wf_*_json_free(char*)` (freed via `cJSON_free`, **not** `free`/`delete`). Each binding wraps these with its native ownership primitive. 3. **Status handling.** The `wf_status` enum (`_result.h`: `WF_OK = 0`, `WF_ERR_INVALID_ARG`, `WF_ERR_ALLOC`, `WF_ERR_NETWORK`, …) maps to the language's native error model; `WF_OK` is success. 4. **Encoding is UTF-8.** All `char*` strings are UTF-8. Marshalling must be explicit; do not rely on platform defaults (UTF-16 in .NET/JS). 5. **Optional modules are feature-gated.** `WOLFRAM_BUILD_SERVER`, `WOLFRAM_BUILD_STORE`, `WOLFRAM_BUILD_STORE_CRYPTO` change which symbols exist in `libwolfram`; bindings gate those surfaces accordingly. 6. **Public headers are `extern "C"`.** Both languages consume the existing `include/wolfram/*.h` directly (C++ includes them; C# parses them with ClangSharp). No hand-maintained signature duplication where a generator fits. ## C++ — `wolfram-cpp` (header-only RAII layer) Implemented as `cpp/wolfram-cpp/` (build with `-DWOLFRAM_BUILD_CPP=ON`; the `wolfram-cpp-smoke` test exercises it offline). C++ includes the C headers directly, so there is **no binding generator** for the wrapper itself — just a thin RAII layer: - `wolfram/unique_handle.hpp` — `unique_handle` RAII owner mirroring wolfram's `wf_T` + `wf_T_free` contract (move-only, `get()`/`release()`/`reset()`). - `wolfram/cstring.hpp` — RAII owner of a heap `char*`; default deleter is `std::free`, pass a `wf_*_json_free` for cJSON-backed strings. - `wolfram/status.hpp` — `wf_status` → `std::error_code` via a custom `error_category`, plus `wolfram::require(wf_status)` that throws `std::system_error`. `wf_status` is `is_error_code_enum`-enabled so it constructs `std::error_code` directly. - `tools/gen_owners.cpp` — scans `include/wolfram/*.h` for `void wf__free(...)` and emits `wolfram/generated_owners.hpp` with one `wf__handle` typedef per owned type (372 handles from 51 headers). Regenerate after changing headers. - `wolfram/wolfram.hpp` — umbrella include. ### Core changes required for C++ consumption The C public headers were made C++-clean as part of adding this wrapper: - Unified the two status enums: `wf_status` (xrpc.h) is now the single canonical enum (full 27-code set, values preserved from the old `wf_error`); `wf_error` (in `_result.h`) is a `typedef` alias of `wf_status`. Previously the same enumerators existed in both enums with divergent values (e.g. `WF_ERR_RATE_LIMIT` was 8 vs 21), which broke any translation unit that included both. - `_result.h` now wraps its declarations in `extern "C"` and no longer has a stray `type` token before `static inline` (a latent bug that also failed to compile in C++). - `project(... LANGUAGES C CXX)` so the C++ target is buildable. ## C# — `Wolfram.Interop` (P/Invoke, AOT-capable) Implemented as `dotnet/Wolfram.Interop/` (build + test with `dotnet test dotnet/Wolfram.Interop.Tests`). Pure pass-through wrappers — no crypto, transport, DAG-CBOR, or server logic is reimplemented; every call forwards to `libwolfram`. - **Raw tier** (`Internal/Raw.cs`) — `LibraryImport` (source-generated, NativeAOT/trim-safe) P/Invoke declarations, 1:1 with the C ABI. UTF-8 strings explicit (`StringMarshalling = Utf8` / `UnmanagedType.LPUTF8Str`), `nuint` for `size_t`, opaque handles as `IntPtr`. A combined `DllImportResolver` loads `libwolfram` (honoring `WOLFRAM_NATIVE_LIB`) and the platform libc (only to free owned strings the SDK returns). - **Managed tier** — `XrpcClientHandle : SafeHandle` owns `wf_xrpc_client*` (released via `wf_xrpc_client_free`; no raw `IntPtr` escapes). `Status` mirrors `wf_status`; `WolframException` is raised on non-`Ok` (honest stubs surface here, never faked). `Wolfram` / `WolframClient` give an idiomatic API. - **Regeneration** — `tools/generate.rsp` is a ClangSharpPInvokeGenerator response file to regenerate/expand the raw tier from `include/wolfram/*.h` (the hand-written `Raw.cs` currently covers the wrapped functions). ## Layout - `cpp/wolfram-cpp/` — header-only C++ RAII wrapper (links `libwolfram`). - `dotnet/Wolfram.Interop/` — raw P/Invoke tier + hand-written managed layer (links `libwolfram`). Both consume the same built `libwolfram`; neither ships crypto/transport code.