Files
zeekay f15816a8b2 docs: rewrite Go/Python/Rust/C++ SDK + quick-start against real runtime
Replace the fictional MCP-style tool-calling API (Zap.connect / call_tool /
Gateway / ToolCall) and wrong package names with the verified zero-copy wire +
capability RPC surface, every name checked against source.

- go.mdx: github.com/zap-proto/[email protected]. Builder→Parse round-trip, zapgen
  codegen (NewPing/WrapPing/PingInput), the rpc call envelope
  (BuildRequest/ParseResponse + 3-value pipelining client), and the cap layer
  (Issue/Attenuate/Verifier.Verify). Compiled+ran a generated Calculator
  client/server end to end (prints 15).
- python.mdx: PyPI zap-proto, import zap (not zap_protocol). Wire round-trip,
  zap.rpc (Server/Client/Method — ran end to end), zap.cap (issue/attenuate/
  Verifier — ran issue+verify), and the [app] decorator app (ZAP + Client).
- rust.mdx: zap-schema crate (zapc bin) + the zap-proto/rust workspace crates
  (zap/zapc/zap-rpc/zap-futures). Real zap-rpc Server trait + RpcSystem client
  + zap::cap (issue/Verifier). Drops nonexistent zap-proto/zap-codegen crates,
  spec-rs, zap_protocol module.
- cpp.mdx: separates the cpp-core schema front-end + zero-copy zap:: runtime
  (MallocMessageBuilder / messageToFlatArray / TwoPartyClient) from the cpp
  high-level binding; drops the fictional zap::connect/callTool client.
- quick-start.mdx: real install/codegen (zapgen, cargo install zap-schema,
  @zap-proto/zapc — not zap-codegen / @zap-proto/codegen / spec/cmd/zap) and
  real generated-RPC server/client per language.
2026-06-21 01:34:27 -07:00

119 lines
4.4 KiB
Plaintext

---
title: C++
description: ZAP C++ SDK — the schema front-end + zero-copy runtime (cpp-core) and the high-level binding (cpp).
---
# C++ Binding
ZAP's C++ surface is split into two repositories with distinct roles:
| Repository | Role |
| --- | --- |
| [`zap-proto/cpp-core`](https://github.com/zap-proto/cpp-core) | The canonical **schema front-end** (the `zap` compiler binary every language plugin execs) **plus** the underlying zero-copy C++ runtime and the KJ support library. Derived from `libcapnp`; ZAP adds the whitespace-significant grammar on top. |
| [`zap-proto/cpp`](https://github.com/zap-proto/cpp) | The high-level C++ **binding** — the client/server convenience API that builds on the `cpp-core` runtime. |
If you are generating code for *another* language, you only need the front-end
from `cpp-core` (it emits a binary code-generator request that your language's
plugin consumes). If you are writing a C++ program, you use the `cpp-core`
runtime headers (in `namespace zap`) plus the `cpp` binding for RPC convenience.
## Build the front-end and runtime
`cpp-core` builds with Autotools, CMake, or Bazel:
```bash
# CMake
cmake -S c++ -B build && cmake --build build
# Autotools
cd c++ && autoreconf -i && ./configure && make -j check
```
`make check` builds the compiler (the `zap_tool` target) and runs the schema,
compiler, and runtime test suites. The `zap` binary is what other runtimes shell
out to.
## Compile a schema
The front-end parses a `.zap` file and emits a binary code-generator request on
stdout; a language plugin consumes it. Plugins are resolved as `zap-<lang>`
executables on `PATH`.
```bash
# Emit a code-generator request to stdout (what a plugin reads on stdin).
zap compile -o- point.zap
# Drive a language plugin found on PATH.
zap compile -orust point.zap
```
ZAP schemas are whitespace-significant — blocks are delimited by indentation,
there are no braces, and ordinals are assigned automatically. The brace form
(`x @0 :Float32;`) still parses, and desugaring is a no-op on it, so both
surfaces produce byte-identical schemas.
## Build and read a message
The runtime lives in `namespace zap` (the `cpp-core` headers). You build a
message with a `MallocMessageBuilder`, initialize the root to your generated
struct type, and read it back through a generated `Reader` — zero-copy, straight
out of the segment memory.
```cpp
#include <zap/message.h>
#include <zap/serialize.h>
#include "point.zap.h" // generated from point.zap
// Write.
zap::MallocMessageBuilder message;
Point::Builder point = message.initRoot<Point>();
point.setX(1.5);
point.setY(-2.5);
kj::Array<zap::word> words = zap::messageToFlatArray(message);
// Read (zero-copy): the reader borrows the flat array; nothing is decoded.
zap::FlatArrayMessageReader reader(words.asPtr());
Point::Reader p = reader.getRoot<Point>();
// p.getX() == 1.5, p.getY() == -2.5
```
`writeMessageToFd` / `StreamFdMessageReader` (in `zap/serialize.h`) stream a
message to and from a file descriptor; `serialize-packed.h` and
`serialize-async.h` provide packed and async framing.
## RPC
The runtime's RPC is capability-based with promise pipelining, exposed through
`zap/rpc-twoparty.h`. `TwoPartyServer` hosts a bootstrap capability over a
connection; `TwoPartyClient` connects and retrieves it.
```cpp
#include <zap/rpc-twoparty.h>
// Server: host an implementation as the bootstrap capability.
zap::TwoPartyServer server(kj::heap<PointTrackerImpl>());
server.accept(std::move(connection)); // an AsyncIoStream
// Client: connect and call the bootstrap interface.
zap::TwoPartyClient client(connection);
PointTracker::Client tracker = client.bootstrap().castAs<PointTracker>();
auto request = tracker.addPointRequest();
request.setP(point);
auto promise = request.send(); // pipelines; await with KJ's event loop
```
`PointTracker::Server` (generated) is the interface you subclass on the server
side; `PointTracker::Client` (generated) is the typed proxy on the client side.
Because a request's result can be used as the target of another call before it
resolves, dependent calls collapse into a single round trip.
## Documentation
The schema toolchain and core runtime are documented in
[`zap-proto/cpp-core`](https://github.com/zap-proto/cpp-core); the high-level
binding is in [`zap-proto/cpp`](https://github.com/zap-proto/cpp). The
[Schema Language](/docs/schema) and [Code Generation](/docs/codegen) pages cover
the `.zap` surface this front-end parses.