| # Standalone FlatBuffers service generator | |
| For runtime enablement and a complete client/server check, see the | |
| [English guide](../../docs/en/flatbuffers.md) or | |
| [δΈζζε](../../docs/cn/flatbuffers.md). | |
| `brpc_flatc` generates bRPC service bindings. The separate `fb_rpc` transport | |
| implements their channel interface in `brpc::Channel`. The generator uses the installed, official | |
| `flatbuffers/idl.h` Parser and `libflatbuffers`; it does not download or modify a | |
| FlatBuffers fork. The official `flatc --cpp` remains responsible for table types. | |
| ## Build and generate | |
| The tool needs a C++11 compiler, CMake, and official FlatBuffers **25.2.10** | |
| headers/library. Use the matching `flatc` with default `--cpp` generation; | |
| options that change C++ naming, such as `--scoped-enums`, are not modeled by | |
| this generator. To build only the generator: | |
| ```sh | |
| cmake -S tools/flatbuffers -B /tmp/brpc-codegen-build -DBUILD_TESTING=OFF | |
| cmake --build /tmp/brpc-codegen-build -j2 | |
| mkdir -p /tmp/brpc-generated | |
| flatc --cpp -o /tmp/brpc-generated test/flatbuffers_codegen/echo.fbs | |
| /tmp/brpc-codegen-build/brpc_flatc -o /tmp/brpc-generated test/flatbuffers_codegen/echo.fbs | |
| ``` | |
| For a non-default installation, add `-DCMAKE_PREFIX_PATH=/path/to/flatbuffers` | |
| and/or explicit `FLATBUFFERS_INCLUDE_DIR` and `FLATBUFFERS_LIBRARY` values. | |
| Run official `flatc` from that matching installation, not an unrelated binary | |
| found earlier on `PATH`. The runtime alone does not link `libflatbuffers`, but | |
| this generator needs it. | |
| The output directory must already exist. The command line is: | |
| ```text | |
| brpc_flatc [-I include_dir]... [-o existing_output_dir] schema.fbs | |
| ``` | |
| For `echo.fbs`, the two tools produce: | |
| - Official `flatc`: `echo_generated.h`. | |
| - `brpc_flatc`: `echo.brpc.fb.h` and `echo.brpc.fb.cpp`, both Apache-licensed. | |
| Compile the generated `.cpp` into the application with C++17 or newer and bRPC | |
| configured with FlatBuffers support. When the detected `Protobuf_VERSION` is | |
| greater than 4.21, also use the matching Protobuf/Abseil dependency set. Keep the generated header, official table header, and bRPC | |
| headers on the include path. The generated service files do not pin a | |
| FlatBuffers version; use an official `flatc` matching your installed table | |
| headers, as required by official generated code. | |
| Schema includes are resolved relative to the input file and through repeated | |
| `-I` options (`-Idir` also works). Run official `flatc --cpp` for each included | |
| schema to obtain its table header. Run `brpc_flatc` separately for each schema | |
| that declares services; imported services are not emitted again. Pass the same | |
| include directories to both tools. This tool processes one `.fbs` per invocation | |
| and follows the official default `*_generated.h` naming convention. | |
| ## Explicit, stable IDs | |
| ```fbs | |
| namespace example.api; | |
| table Request { text:string; } | |
| table Response { text:string; } | |
| rpc_service Echo { | |
| Repeat(Request):Response (id: 41); | |
| Inspect(Request):Response (id: 7); | |
| } | |
| ``` | |
| Every RPC method must have an explicit integer `(id: N)` in `[0, 2147483647]`. | |
| IDs must be unique **within a service**. Missing, negative, duplicate, quoted, | |
| or out-of-range IDs fail generation with a diagnostic and nonzero exit status. | |
| Reordering declarations never changes their wire IDs. This does not require | |
| explicit IDs on FlatBuffers table fields. | |
| The generated names are `example::api::Echo`, | |
| `example::api::Echo_Stub`, and the alias `example::api::Echo::Stub`. | |
| They are **not** the old `EchoStub` spelling. Methods take protobuf | |
| `RpcController`/`Closure` and `brpc::flatbuffers::Message` parameters. Derive from | |
| `Echo` and override the schema methods; pass a `brpc::Channel` initialized with | |
| `ChannelOptions::protocol = "fb_rpc"` to a stub, or any other implementation of | |
| `brpc::flatbuffers::RpcChannel`. Register the implementation with | |
| `Server::AddFlatBuffersService`. See [FlatBuffers RPC](../../docs/en/flatbuffers.md) | |
| for ownership, protocol limitations, and response verification. Channel ownership | |
| defaults to borrowed; `Service::STUB_OWNS_CHANNEL` transfers ownership to the stub. | |
| This intentionally bounded generator supports unary table RPCs, multiple | |
| services/methods, namespaces, included request/response tables, and absent | |
| optional strings. Streaming and C++ keyword names in service/type/namespace | |
| positions are rejected rather than silently misgenerated. Method names that | |
| collide with generated service APIs are also rejected. Services cannot be named | |
| `Stub`, `descriptor`, `GetDescriptor`, or `FBCallMethod`, because these names | |
| are also members of the generated service class. Generated service and stub | |
| class names must be distinct from other services, schema types, table builder | |
| classes, unscoped enum values, union traits, table creation functions, and | |
| namespace prefixes, including those in imported schemas. C++ reserved | |
| identifiers are rejected in the scopes where they are reserved. A service name | |
| ending in `_` is also rejected because its `_Stub` suffix would produce `__`. | |
| Global service names also cannot shadow | |
| namespaces introduced by the generated header (`brpc`, `butil`, `flatbuffers`, | |
| `google`, or `std`). Schema file basenames may contain ASCII letters, digits, | |
| underscores, dots, and hyphens. The header and source are staged under an | |
| output-directory lock before publication. A failure restores the previous pair | |
| when possible; if rollback also fails, the generator | |
| attempts to remove both final outputs and reports that manual cleanup is needed | |
| when the filesystem refuses. The standalone generator currently requires POSIX | |
| file-locking semantics, including on Linux and macOS. | |
| Service header guards preserve the exact filename and first exported service's | |
| fully-qualified identity. They distinguish punctuation/case and same basenames | |
| in different namespaces without depending on checkout or output paths. Schemas | |
| included together must still define distinct fully-qualified C++ services. | |
| ## Descriptor and completion contracts | |
| - `Echo::descriptor()` owns a `ServiceDescriptor` in a function-local static | |
| RAII holder. C++11 initialization is thread-safe; the holder and its owned | |
| method descriptors are destroyed normally at process exit. There is no | |
| leaked singleton allocation or unsynchronized lazy initialization. | |
| - The descriptor table contains the namespace prefix, service name, ordered | |
| method names, and explicit IDs. Stubs use `method(position)`; service dispatch | |
| switches on the stable `method->index()`. | |
| - Service dispatch checks descriptor ownership and identity, non-null request | |
| and response, and `request->Verify<RequestType>()` before calling user code. | |
| - Invalid calls, null stub channels, and default unimplemented methods call | |
| `controller->SetFailed()` when a controller exists, then run non-null `done` | |
| exactly once. Null controllers and callbacks are tolerated on failure. | |
| - Successful dispatch transfers completion responsibility to the user method. | |
| The generated dispatcher does not run `done` again, so asynchronous service | |
| implementations remain possible. User methods and channel implementations | |
| must themselves honor the exactly-once completion contract. | |
| ## Using another FlatBuffers version | |
| The runtime uses FlatBuffers builder internals that may change between upstream | |
| releases, so the source accepts only 25.2.10. To use another version, build it | |
| into a private prefix, update the version checks, and run the full | |
| verification against that prefix. The steps below use 23.5.26 and were tested | |
| on macOS ARM64 (AppleClang 15) and Ubuntu 22.04 (GCC 11.4, Clang 14). They | |
| cover CMake only; the 23.5.26 Bazel files load `@aspect_rules_js` and `@npm`, | |
| which bRPC does not configure. | |
| Build FlatBuffers. `CMAKE_INSTALL_LIBDIR=lib` keeps the library where | |
| `verify.sh` looks for it: | |
| ```sh | |
| git clone --depth 1 --branch v23.5.26 https://github.com/google/flatbuffers.git /tmp/flatbuffers-23.5.26 | |
| cmake -S /tmp/flatbuffers-23.5.26 -B /tmp/flatbuffers-23.5.26/build \ | |
| -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/tmp/flatbuffers-23.5.26-install \ | |
| -DCMAKE_INSTALL_LIBDIR=lib -DCMAKE_POSITION_INDEPENDENT_CODE=ON \ | |
| -DFLATBUFFERS_BUILD_TESTS=OFF | |
| cmake --build /tmp/flatbuffers-23.5.26/build --target install -j4 | |
| ``` | |
| In the bRPC source tree, update the version checks in | |
| `src/brpc/flatbuffers/message.h`, `tools/flatbuffers/brpc_flatc.cpp` and | |
| `test/flatbuffers_codegen/CMakeLists.txt`: | |
| ```sh | |
| perl -pi -e 's/25\.2\.10/23.5.26/g; | |
| s/(FLATBUFFERS_VERSION_MAJOR != )25\b/${1}23/; | |
| s/(FLATBUFFERS_VERSION_MINOR != )2\b/${1}5/; | |
| s/(FLATBUFFERS_VERSION_REVISION != )10\b/${1}26/' \ | |
| src/brpc/flatbuffers/message.h tools/flatbuffers/brpc_flatc.cpp \ | |
| test/flatbuffers_codegen/CMakeLists.txt | |
| git diff --stat | |
| ``` | |
| The diff should touch 13 lines in these three files. If it does not, the source | |
| has changed since this example was written; review the remaining version | |
| checks by hand. | |
| Regenerate the FRPC metadata header with the new `flatc`; `verify.sh` fails | |
| if it is stale: | |
| ```sh | |
| bash tools/flatbuffers/gen_rpc_meta.sh /tmp/flatbuffers-23.5.26-install/bin/flatc | |
| ``` | |
| Then run the verification with the new prefix: | |
| ```sh | |
| FLATBUFFERS_PREFIX=/tmp/flatbuffers-23.5.26-install \ | |
| bash tools/flatbuffers/verify.sh /tmp/brpc-fb23-verify | |
| ``` | |
| Without `FLATBUFFERS_PREFIX`, the script downloads 25.2.10, which the updated | |
| checks reject. Test your own schemas as well. Other versions may need more than | |
| new version numbers, for example changes for Builder internals or the | |
| generator's naming rules. | |
| ## Pre-submit verification | |
| Local runs and the Linux/macOS codegen CI jobs use the same entry point: | |
| ```sh | |
| CXX=clang++ bash tools/flatbuffers/verify.sh /tmp/brpc-codegen-verify | |
| ``` | |
| Install CMake, a C++ compiler, protobuf, gflags, LevelDB, OpenSSL, zlib, and | |
| Gperftools first. The root unit-test configuration requires Gperftools even | |
| when only the FlatBuffers target is built. | |
| Use `CMAKE_PREFIX_PATH` for non-system dependencies (on macOS, select the desired | |
| Homebrew protobuf prefix). The script fetches SHA-256-pinned official FlatBuffers | |
| 25.2.10 and GTest 1.12.0 archives (URLs and hashes are in `verify.sh`). It enables | |
| `BUILD_UNIT_TESTS`, selects the matching `flatc`, and supplies GTest source through | |
| `BRPC_SYSTEM_GTEST_SOURCE_DIR` with `DOWNLOAD_GTEST=OFF`; no system GTest package | |
| is needed. It builds and runs `brpc_flatbuffers_unittest`, rejecting zero-case, | |
| disabled, or skipped runs, then builds the generator and runs both codegen | |
| acceptance and runtime tests. Missing tests or failures stop the script. | |
| `FLATBUFFERS_PREFIX` may reuse an existing matching installation. | |
| `CODEGEN_RUNTIME=OFF` only builds the generator and runs compile-only acceptance; | |
| it skips GTest, the core bRPC build/unit tests, and runtime tests, even when reusing | |
| a build directory. This is not the full CI check. | |
| For generator changes, add both rejected and accepted schemas, compile the | |
| actual output of both generators, and demonstrate that a new regression fails | |
| before the fix. Check generated symbol families and global/nested/imported | |
| scopes rather than only the reported spelling. Record compiler, dependency, | |
| SDK, flags, and source hashes; the script prints tool versions, build directories, | |
| archive hashes, verbose CTest results, and the core GTest XML report path. | |
| The same script does not make different runner/compiler versions equivalent. | |
| Ordinary FlatBuffers-OFF jobs are not codegen evidence. | |
| ## Independent acceptance tests | |
| No root build file needs modification. The default standalone build enables | |
| CTest and additionally needs matching official `flatc`, protobuf, and gflags | |
| development headers. The commands below use CMake/CTest 3.17+ for | |
| `--no-tests=error` (the standalone generator itself has a CMake 3.10 minimum): | |
| ```sh | |
| cmake -S tools/flatbuffers -B /tmp/brpc-codegen-build -DBUILD_TESTING=ON | |
| cmake --build /tmp/brpc-codegen-build -j2 | |
| (cd /tmp/brpc-codegen-build && ctest --no-tests=error --output-on-failure) | |
| ``` | |
| The build compiles the generated service against the real bRPC headers. CTest | |
| rejects missing/duplicate/negative/overflow/string IDs, streaming RPCs, C++20 | |
| keywords, and service/stub names that collide with generated C++ types. | |
| Test-only fault injection covers staging, backup, publication, and rollback | |
| failures; concurrent publishers must also leave a consistent pair. The suite | |
| then compiles the generated service and self-contained header for C++17-or-newer | |
| compatibility, sparse IDs, reordered methods, global/nested | |
| namespaces, zero/max IDs, and included table types. Test configuration headers | |
| and generated artifacts stay in the standalone build directory, not in the | |
| source tree. The name-shadowing cases are compile-only by default; pass | |
| `RUNTIME_LIBRARIES` when invoking `acceptance.cmake` directly to exercise their | |
| runtime checks as well. | |
| Expect **one** CTest named `flatbuffers_codegen_acceptance` in this mode. Its | |
| inner checks also compile colliding-basename headers alone and together in both | |
| orders, and compare regenerated bytes after relocating the schema. An empty | |
| CTest selection is not acceptance. | |
| To enable additional behavior tests against an **already built** | |
| FlatBuffers-enabled bRPC library, use matching runtime headers/dependencies and | |
| an existing library. A shared library avoids having to reconstruct all static | |
| platform link dependencies. Build it with root `BUILD_SHARED_LIBS=ON` and target | |
| `brpc-shared` first; use `.dylib` on macOS instead of `.so`: | |
| ```sh | |
| BRPC_LIBRARY=/absolute/path/to/FB-on/output/lib/libbrpc.so | |
| cmake -S tools/flatbuffers -B /tmp/brpc-codegen-build \ | |
| -DBUILD_TESTING=ON \ | |
| -DBRPC_CODEGEN_BRPC_LIBRARY="$BRPC_LIBRARY" | |
| cmake --build /tmp/brpc-codegen-build -j2 | |
| (cd /tmp/brpc-codegen-build && ctest --no-tests=error --output-on-failure) | |
| ``` | |
| Expect **two** CTests in this mode: `flatbuffers_codegen_acceptance` and | |
| `flatbuffers_codegen_runtime`. For non-default dependency installations, pass | |
| their prefixes through `CMAKE_PREFIX_PATH`; modern Protobuf needs its CMake | |
| config package and Abseil. A static `libbrpc.a` is also accepted, but the caller | |
| must supply its complete transitive/platform link requirements (including macOS | |
| frameworks where needed) via `BRPC_CODEGEN_EXTRA_LIBRARIES`. | |
| The runtime test covers concurrent first descriptor access, sparse | |
| stub/dispatch mapping, absent and present strings, invalid/foreign/forged | |
| methods, failed request verification, null arguments, unimplemented methods, | |
| exactly-once error callbacks (including self-deleting closures), deferred | |
| completion, and channel ownership. It uses a local abstract channel; no network | |
| RPC compatibility is claimed. |