# snapdir for C/C++ The C/C++ binding is a header-only RAII wrapper (`snapdir.hpp`) over the snapdir C ABI (`snapdir.h`), which in turn wraps the canonical `snapdir-api` Rust core. Manifests and snapshot IDs are **bit-identical** to the `snapdir` CLI and to every other binding — a `` you compute here is the exact 64-char lowercase hex string you get from `snapdir id`. ## Install There is no C++ package registry. The published artifact is the [`snapdir-ffi`](https://crates.io/crates/snapdir-ffi) **source crate** on crates.io — you build the static library, generate the C header with `cbindgen`, and compile your program against it with `snapdir.hpp` (a **build-from-source** binding): ```sh cargo build --release # -> target/release/libsnapdir_ffi.a cbindgen --config cbindgen.toml --crate snapdir-ffi --output snapdir.h . g++ -std=c++20 app.cpp -I. -L. -lsnapdir_ffi -lpthread -ldl -lm -o app ``` You need a C++17 compiler (C++20 tested), a Rust toolchain, and `cbindgen`. See [Install snapdir for C/C++](/install/cpp/) for details. ## Usage Synchronous `id` / `manifest`; async `push` / `pull` / `fetch` / `diff` return a `std::future` that resolves or throws on `.get()`. The store URI is always an argument — `file://$PWD/store` for local, `gs://bucket/prefix` for Google Cloud Storage (also S3, Backblaze B2, and SSH). ```cpp #include #include int main() { const std::string store = "file:///tmp/store"; // Synchronous: the 64-char snapshot id and the full manifest. const std::string id = snapdir::id("./my-dir"); snapdir::Manifest m = snapdir::manifest("./my-dir"); for (const snapdir::ManifestEntry &e : m.entries) { // e.type (PathType), e.perm, e.checksum, e.size, e.path } // snapdir::id_from_manifest(m) == id // Options mirror the C ABI defaults. snapdir::ManifestOptions opts; opts.no_follow = true; // do not follow symlinks opts.exclude = std::string("\\.tmp$"); // extended-regex exclusion // Async transfer ops. const std::string pushed = snapdir::push("./my-dir", store).get(); // snapdir::pull(pushed, store, "./restored").get(); snapdir::fetch(pushed, store).get(); // fetch into the local catalog // Diff two stores; each ref follows the store@id convention // (the last '@' splits the store URI from the 64-hex id). std::vector changes = snapdir::diff("file:///tmp/store-a", "file:///tmp/store-b").get(); for (const auto &e : changes) { // e.status is one of A / D / M / = ; e.path is the entry path std::cout << static_cast(e.status) << '\t' << e.path << '\n'; } } ``` ## Errors Every operation throws `snapdir::Error` (a `std::runtime_error`) on failure. The RAII guards release every C allocation even when an exception unwinds. `Error::code()` returns one of the stable, `SCREAMING_SNAKE_CASE` codes shared across all bindings: ```cpp try { auto id = snapdir::id("/no/such/path"); } catch (const snapdir::Error &e) { // e.code() is one of: IO_ERROR, HASH_MISMATCH, STORE_ERROR, IN_FLUX, // CATALOG_ERROR, INVALID_ID, INVALID_STORE, CONFLICT. std::fprintf(stderr, "snapdir error [%s]: %s\n", e.code().c_str(), e.what()); } ``` | Code | Meaning | | --- | --- | | `IO_ERROR` | A filesystem read/write failed. | | `HASH_MISMATCH` | Stored content did not match its expected checksum. | | `STORE_ERROR` | The object store rejected or failed a request. | | `IN_FLUX` | The source tree changed while being snapshotted. | | `CATALOG_ERROR` | The local catalog is missing or corrupt. | | `INVALID_ID` | The snapshot id is not 64-char lowercase hex. | | `INVALID_STORE` | The store URI is malformed or unsupported. | | `CONFLICT` | A concurrent write conflicted with this operation. | ## Platform support Every binding runs on Linux (glibc and Alpine/musl, x64 and arm64) and macOS; Windows is unsupported, since snapdir is Unix-only. | Platform | Supported | | --- | --- | | Linux glibc (x64 + arm64) | yes | | Linux musl / Alpine (x64 + arm64) | yes | | macOS | yes | | Windows | no | The one binding-wide exception is **Java**, which is glibc-only today (musl support is planned for 1.11.1). The C/C++ binding builds and links on glibc and musl alike.