Standalone C++ example: validate an ONNX model with onnx_light checker#

This page documents examples/check_onnx_light_model (view on GitHub), a self-contained CMake project that demonstrates linking with onnx-light and running onnx::checker::check_model() from C++. The same program also demonstrates calling onnx_optim::shapes::InferShapesModel() — the onnx_optim shape-inference entry point — on the loaded model.

Step 1 – Install the C++ library#

From the onnx-light repository root, build and install the static library and its public headers. The Python extension is not required:

cmake -S . -B build-install \
      -DCMAKE_BUILD_TYPE=Release \
      -DONNX_LIGHT_BUILD_PYTHON=OFF \
      -DONNX_LIGHT_BUILD_KERNELS=OFF \
      -DCMAKE_INSTALL_PREFIX=/usr/local
cmake --build build-install
cmake --install build-install

-DONNX_LIGHT_BUILD_KERNELS=OFF skips building lib_onnx_kernels and lib_onnx_backend_test (the operator-kernel runtime and the backend-test case registry). They are not needed by this example, which only links the checker / shape-inference layer exposed by onnx_light::onnx_light.

Step 2 – Build the example#

Point CMAKE_PREFIX_PATH at the install prefix chosen above:

cmake -S examples/check_onnx_light_model -B build-check-onnx-light-model \
      -DCMAKE_BUILD_TYPE=Release \
      -DCMAKE_PREFIX_PATH=/usr/local
cmake --build build-check-onnx-light-model

Step 3 – Run the example#

./build-check-onnx-light-model/check_onnx_light_model path/to/model.onnx 1 1

The optional full_check argument accepts 0 (default) or 1. When full_check=1, checker runs additional shape-inference validation.

The optional infer_shapes() argument accepts 0 (default) or 1. When infer_shapes=1, the example loads the model into a ModelProto and calls onnx_optim::shapes::InferShapesModel() to populate graph.value_info and refine graph.output shapes in place, then reports how many entries each list contains.

Example output:

Model is valid: path/to/model.onnx
  full_check: true
  shape inference: ok
    graph.value_info entries: 12
    graph.output entries:     1

CMakeLists.txt#

The example uses find_package and links against the exported onnx_light::onnx_light target. onnx_light::lib_onnx_optim is also linked so the program can call onnx_optim shape inference:

cmake_minimum_required(VERSION 3.15)
project(check_onnx_light_model LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(onnx_light REQUIRED)

add_executable(check_onnx_light_model main.cc)
target_link_libraries(check_onnx_light_model
  PRIVATE onnx_light::onnx_light onnx_light::lib_onnx_optim)

main.cc#

The program calls the path-based checker API and handles validation failures using onnx::checker::ValidationError. When infer_shapes=1 it also loads the model with LoadProtoFromPath() and runs onnx_optim::shapes::InferShapesModel() on the resulting ModelProto.

/**
 * main.cc — Standalone example: validate an ONNX model with the onnx_light
 * checker API and optionally run onnx_optim shape inference on it.
 *
 * Usage:
 *   ./check_onnx_light_model <model.onnx> [full_check] [infer_shapes]
 *
 * When ``infer_shapes`` is set to 1, the model is loaded into a ``ModelProto``
 * and passed to :cpp:func:`onnx_optim::shapes::InferShapesModel`, which
 * mutates the graph in place so that its outputs and value_info entries carry
 * the inferred shapes. The example then prints how many value_info entries
 * the inferred graph contains.
 *
 * See CMakeLists.txt for build instructions.
 */

#include "onnx_lib/checker.h"
#include "onnx_lib/common/file_utils.h"
#include "onnx_optim/shapes/shape_inference.h"
#include "onnx_proto/onnx.h"

#include <charconv>
#include <iostream>
#include <string_view>

namespace {

bool ParseZeroOrOne(const char *text, bool &value) {
  const std::string_view arg(text);
  if (arg.empty()) {
    return false;
  }

  int parsed = 0;
  const char *begin = arg.data();
  const char *end = begin + arg.size();
  const auto result = std::from_chars(begin, end, parsed);
  if (result.ec != std::errc() || result.ptr != end || (parsed != 0 && parsed != 1)) {
    return false;
  }

  value = (parsed == 1);
  return true;
}

} // namespace

int main(int argc, char *argv[]) {
  if (argc < 2 || argc > 4) {
    std::cerr << "Usage: " << argv[0] << " <model.onnx> [full_check] [infer_shapes]\n";
    std::cerr << "  full_check:   0 (default) or 1\n";
    std::cerr << "  infer_shapes: 0 (default) or 1 — runs onnx_optim shape inference\n";
    return 1;
  }

  bool full_check = false;
  if (argc >= 3 && !ParseZeroOrOne(argv[2], full_check)) {
    std::cerr << "Invalid full_check value: " << argv[2] << " (expected 0 or 1)\n";
    return 1;
  }

  bool infer_shapes = false;
  if (argc == 4 && !ParseZeroOrOne(argv[3], infer_shapes)) {
    std::cerr << "Invalid infer_shapes value: " << argv[3] << " (expected 0 or 1)\n";
    return 1;
  }

  try {
    ONNX_LIGHT_NAMESPACE::checker::check_model(argv[1], full_check);
    std::cout << "Model is valid: " << argv[1] << "\n";
    std::cout << "  full_check: " << (full_check ? "true" : "false") << "\n";

    if (infer_shapes) {
      ONNX_LIGHT_NAMESPACE::ModelProto model;
      ONNX_LIGHT_NAMESPACE::LoadProtoFromPath(argv[1], model);
      ONNX_LIGHT_NAMESPACE::onnx_optim::shapes::InferShapesModel(model);
      std::cout << "  shape inference: ok\n";
      std::cout << "    graph.value_info entries: " << model.graph().value_info_size() << "\n";
      std::cout << "    graph.output entries:     " << model.graph().output_size() << "\n";
    }
  } catch (const ONNX_LIGHT_NAMESPACE::checker::ValidationError &e) {
    std::cerr << "Validation error in '" << argv[1] << "':\n" << e.what() << "\n";
    return 2;
  } catch (const std::exception &e) {
    std::cerr << "Error while processing '" << argv[1] << "': " << e.what() << "\n";
    return 1;
  }

  return 0;
}

See also#