Porting the ONNX C++ library on top of onnx_proto#
- Date:
2026-06
complete
Objective#
Building onnx_proto, the protobuf-free ONNX schema built the protobuf-free message layer
(lib_onnx_proto): the ModelProto / GraphProto / TensorProto
hierarchy, the binary streams, and a wire-compatible parser and serializer.
Messages alone are not enough — the upstream onnx package also provides
the library built on those messages: the operator schema registry, the model
checker, shape inference, the version converter, the function inliner, and the
text parser/printer.
The objective was to port that library so it compiles and runs against
onnx_proto instead of libprotobuf, exposes the same C++ API surface as
upstream ONNX (so existing consumers link it unchanged), and carries no
protobuf dependency of its own.
Post-mortem#
The port keeps upstream’s public entry points and internal structure and swaps only the message layer underneath, using compatibility headers so the source diff against upstream stays small.
One library: lib_onnx_lib#
onnx_light/onnx_lib builds the lib_onnx_lib target from the ported
sources: the operator definitions under defs, checker.cc,
shape_inference, version_converter, inliner, and the IR/proto
converter in common. It links lib_onnx_proto (messages and wire
format) and lib_onnx_manipulations (helper utilities) and nothing from
protobuf. The library can be built STATIC for pure C++ consumers or SHARED
when the Python bindings are enabled.
The protobuf drop-in#
Upstream code includes <onnx/onnx_pb.h> and occasionally names
google::protobuf types (RepeatedField, RepeatedPtrField, the I/O
streams). Rather than rewrite those call sites, the port supplies drop-in
headers: onnx_lib/common/onnx_pb.h replaces the protobuf-generated header,
onnx_proto/google_protobuf_compat.h aliases the google::protobuf names
onto concrete onnx_proto types, and the forwarding tree in
onnx_compat_include resolves onnx/... and google/protobuf/...
includes to those replacements. Because ONNX_LIGHT_NAMESPACE mirrors the
upstream onnx namespace, onnx::checker::check_model and
onnx::shape_inference::InferShapes keep the same spelling for callers.
What was ported#
Operator schemas (
defs/) —OpSchema(formal parameters, type constraints, attributes, domain/version) and theOpSchemaRegistrysingleton, populated through theONNX_OPERATOR_SCHEMAstatic-registration macro. The definitions are grouped by domain family (math, nn, rnn, sequence, quantization, …), with theai.onnx.mloperators compiled only whenONNX_MLis set.Checker (
checker.cc) —check_modeland friends, with aValidationErrorthat appends lexical context as it unwinds.Shape inference (
shape_inference/) — per-node and whole-model type/shape inference with the symbol table, graph inferencer, and inference context implementations.Version converter (
version_converter/) — the adapter chain that migrates a model between opset versions, one adapter per operator transition.Inliner (
inliner/) — local-function inlining with cyclic-reference detection.Parser / printer — the ONNX text format, reachable from C++ and from the Python bindings that wrap it.
What worked#
Preserving upstream’s public API meant a project already using ONNX’s C++ library could link
lib_onnx_libinstead, exactly as Integrating onnx-light into onnxruntime (PR #29723) did for onnxruntime.Confining the substitution to a handful of compatibility headers (
onnx_pb.h,google_protobuf_compat.h, theonnx_compat_includetree) kept the diff against upstream small and mechanical, which makes future re-syncs tractable.Keeping the schema registry, checker, shape inference, and converter as one library matched upstream’s layering and let each component call the others without new seams.
Building on the wire-compatible
onnx_protolayer meant the checker and shape inference operated on byte-identical models, so their results can be compared directly against upstream ONNX.
What remains#
The ported surface covers the parts of the ONNX C++ library that onnx-light
consumers rely on; less-used corners of the upstream API are added on demand.
The compatibility shims expose only the google::protobuf names the ported
code actually uses, so unusual direct protobuf usage in third-party code may
need a small addition to the alias headers.
See also#
Building onnx_proto, the protobuf-free ONNX schema — the protobuf-free message layer this library is built on.
Integrating onnx-light into onnxruntime (PR #29723) — using the same drop-in headers to build onnxruntime against onnx-light.