From e1345463e7e153c00f2f3c6ee58e2b8403a5f3ac Mon Sep 17 00:00:00 2001 From: edgchen1 <18449977+edgchen1@users.noreply.github.com> Date: Tue, 16 Jun 2026 20:23:05 -0700 Subject: [PATCH 1/4] Split experimental API consumer headers and add throwing accessors Move the C++ accessor wrappers out of the experimental C header into a new C++ companion header (onnxruntime_experimental_cxx_api.h), keeping onnxruntime_experimental_c_api.h pure C. Add a throwing accessor variant (Get_..._FnOrThrow) alongside the existing nullable accessor; it throws Ort::Exception (ORT_NOT_IMPLEMENTED) when the experimental function is unavailable. Split the mechanical, X-macro-generated per-function plumbing into _fns.h detail headers so each public header stays focused on its curated, hand-written surface. Enforce that the detail headers are only included through their public header via a scoped ORT_INCLUDING_* guard macro that triggers a #error otherwise. Register the new headers in cmake, update the design doc, and update the tests to use the new headers and the throwing accessor. Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- cmake/onnxruntime.cmake | 3 + docs/design/Experimental_C_API.md | 129 +++++++++++++++--- .../session/onnxruntime_experimental_c_api.h | 74 ++-------- .../onnxruntime_experimental_c_api_fns.h | 39 ++++++ .../onnxruntime_experimental_cxx_api.h | 50 +++++++ .../onnxruntime_experimental_cxx_api_fns.h | 87 ++++++++++++ onnxruntime/test/autoep/test_model_package.cc | 73 +++++----- .../test/shared_lib/test_experimental_api.cc | 10 +- 8 files changed, 341 insertions(+), 124 deletions(-) create mode 100644 include/onnxruntime/core/session/onnxruntime_experimental_c_api_fns.h create mode 100644 include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h create mode 100644 include/onnxruntime/core/session/onnxruntime_experimental_cxx_api_fns.h diff --git a/cmake/onnxruntime.cmake b/cmake/onnxruntime.cmake index 64b31f665f56a..81218e3d5cff2 100644 --- a/cmake/onnxruntime.cmake +++ b/cmake/onnxruntime.cmake @@ -32,6 +32,9 @@ function(get_c_cxx_api_headers HEADERS_VAR) "${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_error_code.h" "${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_experimental_c_api.h" "${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_experimental_c_api.inc" + "${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_experimental_c_api_fns.h" + "${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h" + "${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api_fns.h" "${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_float16.h" "${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_lite_custom_op.h" "${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_run_options_config_keys.h" diff --git a/docs/design/Experimental_C_API.md b/docs/design/Experimental_C_API.md index ba7178ac33460..cc008d05a06ff 100644 --- a/docs/design/Experimental_C_API.md +++ b/docs/design/Experimental_C_API.md @@ -103,17 +103,50 @@ ORT_EXPERIMENTAL_API(22, OrtStatusPtr, OrtApi_AnotherThing, _In_ const OrtEnv* env, _In_ const char* name, _Out_ OrtValue** out) ``` -### Experimental Consumer Header (generated from `.inc`) +### Experimental Consumer Headers (generated from `.inc`) + +Four headers serve experimental API consumers, mirroring the `onnxruntime_c_api.h` / `onnxruntime_cxx_api.h` split. Each +public header is paired with a `_fns.h` detail header that holds only the mechanical, X-macro-generated per-function +plumbing, so the public header stays focused on the curated, hand-written surface: + +- `onnxruntime_experimental_c_api.h` — pure C, public. Holds hand-written auxiliary declarations (e.g. opaque types the + experimental APIs require) and includes the C detail header. + - `onnxruntime_experimental_c_api_fns.h` — detail header: the X-macro pass that produces the function pointer + typedefs and name constants. Not included directly by consumers. +- `onnxruntime_experimental_cxx_api.h` — C++ companion, public. Includes the C header (and `onnxruntime_cxx_api.h`), + includes the C++ detail header, and is where hand-written C++ helpers live. + - `onnxruntime_experimental_cxx_api_fns.h` — detail header: the X-macro passes that produce the typed accessors in + the `Ort::Experimental` namespace. Not included directly by consumers. + +The `.inc` file remains the single source of truth; the `_fns.h` headers are "do not hand-edit except the macro +template." Splitting the generated plumbing out keeps each public header as the place a contributor edits by hand +(auxiliary type declarations, convenience helpers) without wading through macro expansions. Direct inclusion of a +`_fns.h` header triggers a `#error`: each public header defines a short `ORT_INCLUDING_*` guard macro around its detail +include, and the detail header checks for it. -A single header serves both C and C++ experimental API consumers. The C section provides typedefs and name -constants; the C++ section (guarded by `#ifdef __cplusplus`) adds typed inline accessors in the `Ort::Experimental` -namespace. +```c +// onnxruntime_experimental_c_api.h (public) +#pragma once + +#include "onnxruntime_c_api.h" + +// Declare any new, auxiliary opaque types required by the experimental APIs here, before the detail include. +// ORT_RUNTIME_CLASS(...); + +// Per-function typedefs and name constants (X-macro pass over the .inc) live in the detail header. The define/undef +// guard enforces that the detail header is only included through this header. +#define ORT_INCLUDING_EXPERIMENTAL_C_API_FNS +#include "onnxruntime_experimental_c_api_fns.h" +#undef ORT_INCLUDING_EXPERIMENTAL_C_API_FNS +``` ```c -// onnxruntime_experimental_c_api.h +// onnxruntime_experimental_c_api_fns.h (detail — included by the header above) #pragma once -// Declare any new, auxiliary opaque types required by the experimental APIs in this header too. +#ifndef ORT_INCLUDING_EXPERIMENTAL_C_API_FNS +#error "Include onnxruntime_experimental_c_api.h; do not include this detail header directly." +#endif // --- C: function pointer typedefs and name constants --- #define ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) \ @@ -127,35 +160,82 @@ namespace. // ...) NO_EXCEPTION; // static const char* const kOrtExperimental_OrtApi_SomeNewThing_SinceV22_FnName = // "OrtApi_SomeNewThing_SinceV22"; +``` + +The C++ header generates two accessor flavors per function: a nullable accessor (returns `nullptr` if the function is +unavailable) and a throwing accessor (`...FnOrThrow`, throws `Ort::Exception` with `ORT_NOT_IMPLEMENTED` if the function +is unavailable). The nullable accessor is for runtime availability checks; the throwing accessor is for when the +function is required. + +```cpp +// onnxruntime_experimental_cxx_api.h (public) +#pragma once + +#include "onnxruntime_experimental_c_api.h" +#include "onnxruntime_cxx_api.h" // for Ort::Exception / ORT_CXX_API_THROW + +// Typed accessors (nullable + throwing) live in the detail header, which declares them in Ort::Experimental. +// Hand-written C++ helpers can be added in their own Ort::Experimental namespace block. +#define ORT_INCLUDING_EXPERIMENTAL_CXX_API_FNS +#include "onnxruntime_experimental_cxx_api_fns.h" +#undef ORT_INCLUDING_EXPERIMENTAL_CXX_API_FNS +``` + +```cpp +// onnxruntime_experimental_cxx_api_fns.h (detail — included by the header above) +#pragma once + +#ifndef ORT_INCLUDING_EXPERIMENTAL_CXX_API_FNS +#error "Include onnxruntime_experimental_cxx_api.h; do not include this detail header directly." +#endif -#ifdef __cplusplus namespace Ort { namespace Experimental { -// --- C++: typed inline accessors (reuses the C typedefs above) --- -#define ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) \ - inline OrtExperimental_##NAME##_SinceV##VER##_Fn Get_##NAME##_SinceV##VER##_Fn( \ - const OrtApi* api) { \ - return reinterpret_cast( \ - api->GetExperimentalFunction(kOrtExperimental_##NAME##_SinceV##VER##_FnName)); \ +// --- C++: nullable typed inline accessors (reuses the C typedefs) --- +#define ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) \ + inline OrtExperimental_##NAME##_SinceV##VER##_Fn Get_##NAME##_SinceV##VER##_Fn( \ + const OrtApi* api) { \ + return reinterpret_cast( \ + api->GetExperimentalFunction(kOrtExperimental_##NAME##_SinceV##VER##_FnName)); \ } #include "onnxruntime_experimental_c_api.inc" #undef ORT_EXPERIMENTAL_API -} // namespace Experimental -} // namespace Ort +// --- C++: throwing typed inline accessors (reuse the nullable accessors) --- +#define ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) \ + inline OrtExperimental_##NAME##_SinceV##VER##_Fn Get_##NAME##_SinceV##VER##_FnOrThrow( \ + const OrtApi* api) { \ + auto* fn = Get_##NAME##_SinceV##VER##_Fn(api); \ + if (fn == nullptr) { \ + ORT_CXX_API_THROW( \ + "Experimental function " #NAME "_SinceV" #VER " is not available in this build", \ + ORT_NOT_IMPLEMENTED); \ + } \ + return fn; \ + } +#include "onnxruntime_experimental_c_api.inc" +#undef ORT_EXPERIMENTAL_API // Produces (for SinceVersion=22, Name=OrtApi_SomeNewThing): -// namespace Ort { -// namespace Experimental { // inline OrtExperimental_OrtApi_SomeNewThing_SinceV22_Fn // Get_OrtApi_SomeNewThing_SinceV22_Fn(const OrtApi* api) { // return reinterpret_cast( // api->GetExperimentalFunction(kOrtExperimental_OrtApi_SomeNewThing_SinceV22_FnName)); // } -// } -// } -#endif // __cplusplus +// inline OrtExperimental_OrtApi_SomeNewThing_SinceV22_Fn +// Get_OrtApi_SomeNewThing_SinceV22_FnOrThrow(const OrtApi* api) { +// auto* fn = Get_OrtApi_SomeNewThing_SinceV22_Fn(api); +// if (fn == nullptr) { +// ORT_CXX_API_THROW( +// "Experimental function OrtApi_SomeNewThing_SinceV22 is not available in this build", +// ORT_NOT_IMPLEMENTED); +// } +// return fn; +// } + +} // namespace Experimental +} // namespace Ort ``` C usage: @@ -169,7 +249,7 @@ if (fn) { } ``` -C++ usage: +C++ usage (nullable): ```cpp if (auto* fn = Ort::Experimental::Get_OrtApi_SomeNewThing_SinceV22_Fn(api)) { @@ -177,6 +257,13 @@ if (auto* fn = Ort::Experimental::Get_OrtApi_SomeNewThing_SinceV22_Fn(api)) { } ``` +C++ usage (throwing): + +```cpp +auto* fn = Ort::Experimental::Get_OrtApi_SomeNewThing_SinceV22_FnOrThrow(api); +Ort::Status status(fn(session, &result)); +``` + ### Implementation Side (generated from `.inc`) ```cpp diff --git a/include/onnxruntime/core/session/onnxruntime_experimental_c_api.h b/include/onnxruntime/core/session/onnxruntime_experimental_c_api.h index 0dd87c10776d3..0735f3e524803 100644 --- a/include/onnxruntime/core/session/onnxruntime_experimental_c_api.h +++ b/include/onnxruntime/core/session/onnxruntime_experimental_c_api.h @@ -3,11 +3,16 @@ // Experimental C API consumer header. // -// This header provides typedefs, name constants, and (for C++) typed inline accessors for experimental ORT functions. +// This header provides C function pointer typedefs and name constants for experimental ORT functions. // It should be used together with the experimental header lookup function `OrtApi::GetExperimentalFunction()`. // -// This header contains code generated from onnxruntime_experimental_c_api.inc, which defines the list of experimental -// API functions. +// For C++ consumers, the companion header onnxruntime_experimental_cxx_api.h provides typed inline accessors in the +// Ort::Experimental namespace (including throwing variants). +// +// The per-function typedefs and name constants are produced from onnxruntime_experimental_c_api.inc (which defines the +// list of experimental API functions) by the detail header onnxruntime_experimental_c_api_fns.h, which this header +// includes. Hand-written auxiliary declarations (e.g. opaque types the experimental APIs use) live directly in this +// header. // // IMPORTANT: Experimental functions are NOT part of the stable ABI. They may be added, changed, or removed between // releases without notice. A function's availability should always be checked at runtime (the lookup returns nullptr @@ -21,7 +26,7 @@ // OrtStatusPtr status = fn(&result); // } // -// C++ usage: +// C++ usage (see onnxruntime_experimental_cxx_api.h): // if (auto* fn = Ort::Experimental::Get_OrtApi_ExperimentalApiTest_SinceV28_Fn(api)) { // Ort::Status status(fn(&result)); // } @@ -46,59 +51,10 @@ ORT_RUNTIME_CLASS(ModelPackageComponentContext); // // C: function pointer typedefs and name constants // +// Generated from onnxruntime_experimental_c_api.inc. Kept in a separate detail header so this header stays focused on +// the curated, hand-written surface (auxiliary declarations above, plus any custom helpers). The define/undef guard +// enforces that the detail header is only ever pulled in through this header. -// For each ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) entry in the .inc file, this generates: -// -// // Function pointer typedef: -// typedef RET(ORT_API_CALL* OrtExperimental__SinceV_Fn)(...) NO_EXCEPTION; -// -// // Name constant for lookup: -// static const char* const kOrtExperimental__SinceV_FnName = "_SinceV"; -// -// Example: ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtApi_ExperimentalApiTest, _Out_ int64_t* out) produces: -// typedef OrtStatusPtr(ORT_API_CALL* OrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_Fn)( -// _Out_ int64_t* out) NO_EXCEPTION; -// static const char* const kOrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_FnName = -// "OrtApi_ExperimentalApiTest_SinceV28"; -#define ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) \ - typedef RET(ORT_API_CALL* OrtExperimental_##NAME##_SinceV##VER##_Fn)(__VA_ARGS__) NO_EXCEPTION; \ - static const char* const kOrtExperimental_##NAME##_SinceV##VER##_FnName = #NAME "_SinceV" #VER; - -#include "onnxruntime_experimental_c_api.inc" - -#undef ORT_EXPERIMENTAL_API - -// -// C++: typed inline accessors -// - -#ifdef __cplusplus - -namespace Ort { -namespace Experimental { - -// For each .inc entry, this generates a typed accessor in Ort::Experimental: -// -// inline OrtExperimental__SinceV_Fn Get__SinceV_Fn(const OrtApi* api); -// -// Example: ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtApi_ExperimentalApiTest, ...) produces: -// inline OrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_Fn -// Get_OrtApi_ExperimentalApiTest_SinceV28_Fn(const OrtApi* api) { -// return reinterpret_cast( -// api->GetExperimentalFunction(kOrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_FnName)); -// } -#define ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) \ - inline OrtExperimental_##NAME##_SinceV##VER##_Fn Get_##NAME##_SinceV##VER##_Fn( \ - const OrtApi* api) { \ - return reinterpret_cast( \ - api->GetExperimentalFunction(kOrtExperimental_##NAME##_SinceV##VER##_FnName)); \ - } - -#include "onnxruntime_experimental_c_api.inc" - -#undef ORT_EXPERIMENTAL_API - -} // namespace Experimental -} // namespace Ort - -#endif // __cplusplus +#define ORT_INCLUDING_EXPERIMENTAL_C_API_FNS +#include "onnxruntime_experimental_c_api_fns.h" +#undef ORT_INCLUDING_EXPERIMENTAL_C_API_FNS diff --git a/include/onnxruntime/core/session/onnxruntime_experimental_c_api_fns.h b/include/onnxruntime/core/session/onnxruntime_experimental_c_api_fns.h new file mode 100644 index 0000000000000..43908d2c57721 --- /dev/null +++ b/include/onnxruntime/core/session/onnxruntime_experimental_c_api_fns.h @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Experimental C API function declarations (per-function plumbing). +// +// This is an internal detail header for onnxruntime_experimental_c_api.h. Do NOT include it directly (enforced via the +// #error below) — include onnxruntime_experimental_c_api.h instead. It must be included after onnxruntime_c_api.h (for +// ORT_API_CALL / NO_EXCEPTION) and after any auxiliary type declarations that the experimental functions reference +// (e.g. ORT_RUNTIME_CLASS(...)). +// +// It performs an X-macro pass over onnxruntime_experimental_c_api.inc to declare, for each experimental function, its +// C function pointer typedef and lookup name constant. + +#pragma once + +#ifndef ORT_INCLUDING_EXPERIMENTAL_C_API_FNS +#error "Include onnxruntime_experimental_c_api.h; do not include this detail header directly." +#endif + +// For each ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) entry in the .inc file, this generates: +// +// // Function pointer typedef: +// typedef RET(ORT_API_CALL* OrtExperimental__SinceV_Fn)(...) NO_EXCEPTION; +// +// // Name constant for lookup: +// static const char* const kOrtExperimental__SinceV_FnName = "_SinceV"; +// +// Example: ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtApi_ExperimentalApiTest, _Out_ int64_t* out) produces: +// typedef OrtStatusPtr(ORT_API_CALL* OrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_Fn)( +// _Out_ int64_t* out) NO_EXCEPTION; +// static const char* const kOrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_FnName = +// "OrtApi_ExperimentalApiTest_SinceV28"; +#define ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) \ + typedef RET(ORT_API_CALL* OrtExperimental_##NAME##_SinceV##VER##_Fn)(__VA_ARGS__) NO_EXCEPTION; \ + static const char* const kOrtExperimental_##NAME##_SinceV##VER##_FnName = #NAME "_SinceV" #VER; + +#include "onnxruntime_experimental_c_api.inc" + +#undef ORT_EXPERIMENTAL_API diff --git a/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h b/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h new file mode 100644 index 0000000000000..c35a7b583997b --- /dev/null +++ b/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Experimental C++ API consumer header. +// +// This header provides typed inline accessors in the Ort::Experimental namespace for experimental ORT functions. It is +// the C++ companion to onnxruntime_experimental_c_api.h, which provides the underlying C function pointer typedefs and +// name constants. +// +// The per-function accessors are produced from onnxruntime_experimental_c_api.inc (which defines the list of +// experimental API functions) by the detail header onnxruntime_experimental_cxx_api_fns.h, which this header includes +// inside the Ort::Experimental namespace. Hand-written C++ helpers/wrappers live directly in this header. +// +// IMPORTANT: Experimental functions are NOT part of the stable ABI. They may be added, changed, or removed between +// releases without notice. +// +// Two accessor flavors are generated for each experimental function: +// +// 1. Get__SinceV_Fn(api) -> returns the typed function pointer, or nullptr if the function is not +// available in this build. Use this to check availability at runtime. +// +// 2. Get__SinceV_FnOrThrow(api) -> returns the typed function pointer, or throws Ort::Exception +// (ORT_NOT_IMPLEMENTED) if the function is not available in this build. +// Use this when the function is required. +// +// C++ usage (nullable): +// if (auto* fn = Ort::Experimental::Get_OrtApi_ExperimentalApiTest_SinceV28_Fn(api)) { +// Ort::Status status(fn(&result)); +// } +// +// C++ usage (throwing): +// auto* fn = Ort::Experimental::Get_OrtApi_ExperimentalApiTest_SinceV28_FnOrThrow(api); +// Ort::Status status(fn(&result)); + +#pragma once + +#include "onnxruntime_experimental_c_api.h" +#include "onnxruntime_cxx_api.h" + +// +// Typed accessors (nullable and throwing) +// +// Generated from onnxruntime_experimental_c_api.inc and declared in the Ort::Experimental namespace. Kept in a separate +// detail header so this header stays focused on the curated, hand-written surface. Custom C++ helpers can be added +// below in their own `namespace Ort { namespace Experimental { ... } }` block. The define/undef guard enforces that the +// detail header is only ever pulled in through this header. + +#define ORT_INCLUDING_EXPERIMENTAL_CXX_API_FNS +#include "onnxruntime_experimental_cxx_api_fns.h" +#undef ORT_INCLUDING_EXPERIMENTAL_CXX_API_FNS diff --git a/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api_fns.h b/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api_fns.h new file mode 100644 index 0000000000000..bfcc58cc54b7c --- /dev/null +++ b/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api_fns.h @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// Experimental C++ API accessor declarations (per-function plumbing). +// +// This is an internal detail header for onnxruntime_experimental_cxx_api.h. Do NOT include it directly (enforced via +// the #error below) — include onnxruntime_experimental_cxx_api.h instead. It must be included after +// onnxruntime_experimental_c_api.h (for the C typedefs / name constants) and onnxruntime_cxx_api.h (for Ort::Exception +// / ORT_CXX_API_THROW). +// +// It performs two X-macro passes over onnxruntime_experimental_c_api.inc to declare, for each experimental function, +// a nullable typed accessor and a throwing (FnOrThrow) typed accessor, both in the Ort::Experimental namespace. + +#pragma once + +#ifndef ORT_INCLUDING_EXPERIMENTAL_CXX_API_FNS +#error "Include onnxruntime_experimental_cxx_api.h; do not include this detail header directly." +#endif + +namespace Ort { +namespace Experimental { + +// +// Nullable typed accessors +// + +// For each .inc entry, this generates a typed accessor in Ort::Experimental: +// +// inline OrtExperimental__SinceV_Fn Get__SinceV_Fn(const OrtApi* api); +// +// Example: ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtApi_ExperimentalApiTest, ...) produces: +// inline OrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_Fn +// Get_OrtApi_ExperimentalApiTest_SinceV28_Fn(const OrtApi* api) { +// return reinterpret_cast( +// api->GetExperimentalFunction(kOrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_FnName)); +// } +#define ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) \ + inline OrtExperimental_##NAME##_SinceV##VER##_Fn Get_##NAME##_SinceV##VER##_Fn( \ + const OrtApi* api) { \ + return reinterpret_cast( \ + api->GetExperimentalFunction(kOrtExperimental_##NAME##_SinceV##VER##_FnName)); \ + } + +#include "onnxruntime_experimental_c_api.inc" + +#undef ORT_EXPERIMENTAL_API + +// +// Throwing typed accessors +// + +// For each .inc entry, this generates a throwing accessor in Ort::Experimental: +// +// inline OrtExperimental__SinceV_Fn Get__SinceV_FnOrThrow(const OrtApi* api); +// +// It returns the non-null typed function pointer, or throws Ort::Exception (ORT_NOT_IMPLEMENTED) if the function is +// not available in this build. +// +// Example: ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtApi_ExperimentalApiTest, ...) produces: +// inline OrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_Fn +// Get_OrtApi_ExperimentalApiTest_SinceV28_FnOrThrow(const OrtApi* api) { +// auto* fn = Get_OrtApi_ExperimentalApiTest_SinceV28_Fn(api); +// if (fn == nullptr) { +// ORT_CXX_API_THROW( +// "Experimental function OrtApi_ExperimentalApiTest_SinceV28 is not available in this build", +// ORT_NOT_IMPLEMENTED); +// } +// return fn; +// } +#define ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) \ + inline OrtExperimental_##NAME##_SinceV##VER##_Fn Get_##NAME##_SinceV##VER##_FnOrThrow( \ + const OrtApi* api) { \ + auto* fn = Get_##NAME##_SinceV##VER##_Fn(api); \ + if (fn == nullptr) { \ + ORT_CXX_API_THROW( \ + "Experimental function " #NAME "_SinceV" #VER " is not available in this build", \ + ORT_NOT_IMPLEMENTED); \ + } \ + return fn; \ + } + +#include "onnxruntime_experimental_c_api.inc" + +#undef ORT_EXPERIMENTAL_API + +} // namespace Experimental +} // namespace Ort diff --git a/onnxruntime/test/autoep/test_model_package.cc b/onnxruntime/test/autoep/test_model_package.cc index 34f73eb69b149..ee5c8bb567e1e 100644 --- a/onnxruntime/test/autoep/test_model_package.cc +++ b/onnxruntime/test/autoep/test_model_package.cc @@ -12,7 +12,7 @@ #include "gtest/gtest.h" #include "core/session/model_package/model_package_context.h" -#include "core/session/onnxruntime_experimental_c_api.h" +#include "core/session/onnxruntime_experimental_cxx_api.h" #include "core/session/abi_devices.h" #include "test/autoep/test_autoep_utils.h" #include "test/util/include/asserts.h" @@ -62,47 +62,38 @@ struct ModelPackageFns { inline const ModelPackageFns& GetModelPackageFns() { static const ModelPackageFns fns = []() { const OrtApi* api = &Ort::GetApi(); + namespace Exp = Ort::Experimental; ModelPackageFns f; -#define RESOLVE(member, getter) \ - do { \ - f.member = Ort::Experimental::getter(api); \ - if (f.member == nullptr) { \ - throw std::runtime_error(std::string("Failed to resolve experimental " \ - "OrtModelPackageApi_") + \ - #member "_SinceV28"); \ - } \ - } while (0) - RESOLVE(CreateModelPackageOptionsFromSessionOptions, - Get_OrtModelPackageApi_CreateModelPackageOptionsFromSessionOptions_SinceV28_Fn); - RESOLVE(ReleaseModelPackageOptions, - Get_OrtModelPackageApi_ReleaseModelPackageOptions_SinceV28_Fn); - RESOLVE(CreateModelPackageContext, - Get_OrtModelPackageApi_CreateModelPackageContext_SinceV28_Fn); - RESOLVE(ReleaseModelPackageContext, - Get_OrtModelPackageApi_ReleaseModelPackageContext_SinceV28_Fn); - RESOLVE(ModelPackage_GetSchemaVersion, - Get_OrtModelPackageApi_ModelPackage_GetSchemaVersion_SinceV28_Fn); - RESOLVE(ModelPackage_GetComponentCount, - Get_OrtModelPackageApi_ModelPackage_GetComponentCount_SinceV28_Fn); - RESOLVE(ModelPackage_GetComponentNames, - Get_OrtModelPackageApi_ModelPackage_GetComponentNames_SinceV28_Fn); - RESOLVE(ModelPackage_GetVariantCount, - Get_OrtModelPackageApi_ModelPackage_GetVariantCount_SinceV28_Fn); - RESOLVE(ModelPackage_GetVariantNames, - Get_OrtModelPackageApi_ModelPackage_GetVariantNames_SinceV28_Fn); - RESOLVE(ModelPackage_GetVariantEpName, - Get_OrtModelPackageApi_ModelPackage_GetVariantEpName_SinceV28_Fn); - RESOLVE(SelectComponent, - Get_OrtModelPackageApi_SelectComponent_SinceV28_Fn); - RESOLVE(ReleaseModelPackageComponentContext, - Get_OrtModelPackageApi_ReleaseModelPackageComponentContext_SinceV28_Fn); - RESOLVE(ModelPackageComponent_GetSelectedVariantName, - Get_OrtModelPackageApi_ModelPackageComponent_GetSelectedVariantName_SinceV28_Fn); - RESOLVE(ModelPackageComponent_GetSelectedVariantFolderPath, - Get_OrtModelPackageApi_ModelPackageComponent_GetSelectedVariantFolderPath_SinceV28_Fn); - RESOLVE(CreateSession, - Get_OrtModelPackageApi_CreateSession_SinceV28_Fn); -#undef RESOLVE + f.CreateModelPackageOptionsFromSessionOptions = + Exp::Get_OrtModelPackageApi_CreateModelPackageOptionsFromSessionOptions_SinceV28_FnOrThrow(api); + f.ReleaseModelPackageOptions = + Exp::Get_OrtModelPackageApi_ReleaseModelPackageOptions_SinceV28_FnOrThrow(api); + f.CreateModelPackageContext = + Exp::Get_OrtModelPackageApi_CreateModelPackageContext_SinceV28_FnOrThrow(api); + f.ReleaseModelPackageContext = + Exp::Get_OrtModelPackageApi_ReleaseModelPackageContext_SinceV28_FnOrThrow(api); + f.ModelPackage_GetSchemaVersion = + Exp::Get_OrtModelPackageApi_ModelPackage_GetSchemaVersion_SinceV28_FnOrThrow(api); + f.ModelPackage_GetComponentCount = + Exp::Get_OrtModelPackageApi_ModelPackage_GetComponentCount_SinceV28_FnOrThrow(api); + f.ModelPackage_GetComponentNames = + Exp::Get_OrtModelPackageApi_ModelPackage_GetComponentNames_SinceV28_FnOrThrow(api); + f.ModelPackage_GetVariantCount = + Exp::Get_OrtModelPackageApi_ModelPackage_GetVariantCount_SinceV28_FnOrThrow(api); + f.ModelPackage_GetVariantNames = + Exp::Get_OrtModelPackageApi_ModelPackage_GetVariantNames_SinceV28_FnOrThrow(api); + f.ModelPackage_GetVariantEpName = + Exp::Get_OrtModelPackageApi_ModelPackage_GetVariantEpName_SinceV28_FnOrThrow(api); + f.SelectComponent = + Exp::Get_OrtModelPackageApi_SelectComponent_SinceV28_FnOrThrow(api); + f.ReleaseModelPackageComponentContext = + Exp::Get_OrtModelPackageApi_ReleaseModelPackageComponentContext_SinceV28_FnOrThrow(api); + f.ModelPackageComponent_GetSelectedVariantName = + Exp::Get_OrtModelPackageApi_ModelPackageComponent_GetSelectedVariantName_SinceV28_FnOrThrow(api); + f.ModelPackageComponent_GetSelectedVariantFolderPath = + Exp::Get_OrtModelPackageApi_ModelPackageComponent_GetSelectedVariantFolderPath_SinceV28_FnOrThrow(api); + f.CreateSession = + Exp::Get_OrtModelPackageApi_CreateSession_SinceV28_FnOrThrow(api); return f; }(); return fns; diff --git a/onnxruntime/test/shared_lib/test_experimental_api.cc b/onnxruntime/test/shared_lib/test_experimental_api.cc index d5bdc16ac3f54..893424e5d886c 100644 --- a/onnxruntime/test/shared_lib/test_experimental_api.cc +++ b/onnxruntime/test/shared_lib/test_experimental_api.cc @@ -3,7 +3,7 @@ #include "core/session/onnxruntime_c_api.h" #include "core/session/onnxruntime_cxx_api.h" -#include "core/session/onnxruntime_experimental_c_api.h" +#include "core/session/onnxruntime_experimental_cxx_api.h" #include "gtest/gtest.h" @@ -50,10 +50,14 @@ TEST_F(ExperimentalCApiTest, KnownNameResolvesCpp) { EXPECT_NE(fn, nullptr); } -// Call through typed pointer succeeds and returns the expected sentinel value +// Call through typed pointer succeeds and returns the expected sentinel value. +// Uses the throwing accessor (FnOrThrow), which returns a guaranteed-non-null pointer or throws. +// The throw-on-unavailable path is not directly unit-testable: every .inc entry is registered, so a valid +// typed accessor never observes a nullptr. Availability checking is covered by the nullable-accessor tests above. TEST_F(ExperimentalCApiTest, CallThroughTypedPointer) { - auto* fn = Ort::Experimental::Get_OrtApi_ExperimentalApiTest_SinceV28_Fn(api_); + auto* fn = Ort::Experimental::Get_OrtApi_ExperimentalApiTest_SinceV28_FnOrThrow(api_); ASSERT_NE(fn, nullptr); + EXPECT_EQ(fn, Ort::Experimental::Get_OrtApi_ExperimentalApiTest_SinceV28_Fn(api_)); int64_t result = 0; auto status = Ort::Status{fn(&result)}; From c54cedc25536275f4c5796d5dfaebb0e6a94dbc1 Mon Sep 17 00:00:00 2001 From: edgchen1 <18449977+edgchen1@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:53:00 -0700 Subject: [PATCH 2/4] Collapse experimental consumer headers back to single files Inline the experimental C typedef/name-constant macro pass and C++ nullable/FnOrThrow accessor passes into the public headers. Keep explicit section comments for auxiliary/custom code in each header. Remove obsolete split detail headers and their installation entries from cmake. Update Experimental_C_API.md to document the restored two-header model and inline X-macro expansion approach. Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- cmake/onnxruntime.cmake | 2 - docs/design/Experimental_C_API.md | 60 +++---------- .../session/onnxruntime_experimental_c_api.h | 44 ++++++---- .../onnxruntime_experimental_c_api_fns.h | 39 -------- .../onnxruntime_experimental_cxx_api.h | 88 ++++++++++++++++--- .../onnxruntime_experimental_cxx_api_fns.h | 87 ------------------ 6 files changed, 113 insertions(+), 207 deletions(-) delete mode 100644 include/onnxruntime/core/session/onnxruntime_experimental_c_api_fns.h delete mode 100644 include/onnxruntime/core/session/onnxruntime_experimental_cxx_api_fns.h diff --git a/cmake/onnxruntime.cmake b/cmake/onnxruntime.cmake index 81218e3d5cff2..3b23f71160bf7 100644 --- a/cmake/onnxruntime.cmake +++ b/cmake/onnxruntime.cmake @@ -32,9 +32,7 @@ function(get_c_cxx_api_headers HEADERS_VAR) "${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_error_code.h" "${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_experimental_c_api.h" "${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_experimental_c_api.inc" - "${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_experimental_c_api_fns.h" "${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h" - "${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api_fns.h" "${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_float16.h" "${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_lite_custom_op.h" "${REPO_ROOT}/include/onnxruntime/core/session/onnxruntime_run_options_config_keys.h" diff --git a/docs/design/Experimental_C_API.md b/docs/design/Experimental_C_API.md index cc008d05a06ff..6105e3106c037 100644 --- a/docs/design/Experimental_C_API.md +++ b/docs/design/Experimental_C_API.md @@ -105,24 +105,16 @@ ORT_EXPERIMENTAL_API(22, OrtStatusPtr, OrtApi_AnotherThing, ### Experimental Consumer Headers (generated from `.inc`) -Four headers serve experimental API consumers, mirroring the `onnxruntime_c_api.h` / `onnxruntime_cxx_api.h` split. Each -public header is paired with a `_fns.h` detail header that holds only the mechanical, X-macro-generated per-function -plumbing, so the public header stays focused on the curated, hand-written surface: - -- `onnxruntime_experimental_c_api.h` — pure C, public. Holds hand-written auxiliary declarations (e.g. opaque types the - experimental APIs require) and includes the C detail header. - - `onnxruntime_experimental_c_api_fns.h` — detail header: the X-macro pass that produces the function pointer - typedefs and name constants. Not included directly by consumers. -- `onnxruntime_experimental_cxx_api.h` — C++ companion, public. Includes the C header (and `onnxruntime_cxx_api.h`), - includes the C++ detail header, and is where hand-written C++ helpers live. - - `onnxruntime_experimental_cxx_api_fns.h` — detail header: the X-macro passes that produce the typed accessors in - the `Ort::Experimental` namespace. Not included directly by consumers. - -The `.inc` file remains the single source of truth; the `_fns.h` headers are "do not hand-edit except the macro -template." Splitting the generated plumbing out keeps each public header as the place a contributor edits by hand -(auxiliary type declarations, convenience helpers) without wading through macro expansions. Direct inclusion of a -`_fns.h` header triggers a `#error`: each public header defines a short `ORT_INCLUDING_*` guard macro around its detail -include, and the detail header checks for it. +Two headers serve experimental API consumers, mirroring the `onnxruntime_c_api.h` / `onnxruntime_cxx_api.h` split: + +- `onnxruntime_experimental_c_api.h` — pure C. Provides the function pointer typedefs and name constants, plus any + auxiliary opaque type declarations the experimental APIs require. +- `onnxruntime_experimental_cxx_api.h` — C++ companion. Includes the C header (and `onnxruntime_cxx_api.h`) and adds + typed inline accessors in the `Ort::Experimental` namespace. Any C++ wrapper types associated with the experimental + APIs should also be defined in this header. + +The `.inc` file remains the single source of truth. Each public header performs the appropriate X-macro pass inline, +keeping the declaration list centralized. ```c // onnxruntime_experimental_c_api.h (public) @@ -130,24 +122,9 @@ include, and the detail header checks for it. #include "onnxruntime_c_api.h" -// Declare any new, auxiliary opaque types required by the experimental APIs here, before the detail include. +// Declare any new, auxiliary opaque types required by the experimental APIs in this header too. // ORT_RUNTIME_CLASS(...); -// Per-function typedefs and name constants (X-macro pass over the .inc) live in the detail header. The define/undef -// guard enforces that the detail header is only included through this header. -#define ORT_INCLUDING_EXPERIMENTAL_C_API_FNS -#include "onnxruntime_experimental_c_api_fns.h" -#undef ORT_INCLUDING_EXPERIMENTAL_C_API_FNS -``` - -```c -// onnxruntime_experimental_c_api_fns.h (detail — included by the header above) -#pragma once - -#ifndef ORT_INCLUDING_EXPERIMENTAL_C_API_FNS -#error "Include onnxruntime_experimental_c_api.h; do not include this detail header directly." -#endif - // --- C: function pointer typedefs and name constants --- #define ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) \ typedef RET(ORT_API_CALL* OrtExperimental_##NAME##_SinceV##VER##_Fn)(__VA_ARGS__) NO_EXCEPTION; \ @@ -174,21 +151,6 @@ function is required. #include "onnxruntime_experimental_c_api.h" #include "onnxruntime_cxx_api.h" // for Ort::Exception / ORT_CXX_API_THROW -// Typed accessors (nullable + throwing) live in the detail header, which declares them in Ort::Experimental. -// Hand-written C++ helpers can be added in their own Ort::Experimental namespace block. -#define ORT_INCLUDING_EXPERIMENTAL_CXX_API_FNS -#include "onnxruntime_experimental_cxx_api_fns.h" -#undef ORT_INCLUDING_EXPERIMENTAL_CXX_API_FNS -``` - -```cpp -// onnxruntime_experimental_cxx_api_fns.h (detail — included by the header above) -#pragma once - -#ifndef ORT_INCLUDING_EXPERIMENTAL_CXX_API_FNS -#error "Include onnxruntime_experimental_cxx_api.h; do not include this detail header directly." -#endif - namespace Ort { namespace Experimental { diff --git a/include/onnxruntime/core/session/onnxruntime_experimental_c_api.h b/include/onnxruntime/core/session/onnxruntime_experimental_c_api.h index 0735f3e524803..191928b88ca88 100644 --- a/include/onnxruntime/core/session/onnxruntime_experimental_c_api.h +++ b/include/onnxruntime/core/session/onnxruntime_experimental_c_api.h @@ -3,20 +3,18 @@ // Experimental C API consumer header. // -// This header provides C function pointer typedefs and name constants for experimental ORT functions. -// It should be used together with the experimental header lookup function `OrtApi::GetExperimentalFunction()`. +// This header provides C function pointer typedefs and name constants for experimental ORT API functions, as well as +// any auxiliary declarations required by the experimental API functions. // -// For C++ consumers, the companion header onnxruntime_experimental_cxx_api.h provides typed inline accessors in the -// Ort::Experimental namespace (including throwing variants). +// The function pointer typedefs and name constants should be used together with the experimental API lookup function +// `OrtApi::GetExperimentalFunction()`. A function's availability should always be checked at runtime (the lookup +// returns nullptr if the function is not present). // -// The per-function typedefs and name constants are produced from onnxruntime_experimental_c_api.inc (which defines the -// list of experimental API functions) by the detail header onnxruntime_experimental_c_api_fns.h, which this header -// includes. Hand-written auxiliary declarations (e.g. opaque types the experimental APIs use) live directly in this -// header. +// C++ API consumers should use the companion header, onnxruntime_experimental_cxx_api.h, which provides typed +// accessors for the experimental API functions. // // IMPORTANT: Experimental functions are NOT part of the stable ABI. They may be added, changed, or removed between -// releases without notice. A function's availability should always be checked at runtime (the lookup returns nullptr -// if the function is not present). +// releases without notice. Anything in this file should be treated as experimental and unstable. // // C usage: // OrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_Fn fn = @@ -51,10 +49,24 @@ ORT_RUNTIME_CLASS(ModelPackageComponentContext); // // C: function pointer typedefs and name constants // -// Generated from onnxruntime_experimental_c_api.inc. Kept in a separate detail header so this header stays focused on -// the curated, hand-written surface (auxiliary declarations above, plus any custom helpers). The define/undef guard -// enforces that the detail header is only ever pulled in through this header. -#define ORT_INCLUDING_EXPERIMENTAL_C_API_FNS -#include "onnxruntime_experimental_c_api_fns.h" -#undef ORT_INCLUDING_EXPERIMENTAL_C_API_FNS +// For each ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) entry in the .inc file, this generates: +// +// // Function pointer typedef: +// typedef RET(ORT_API_CALL* OrtExperimental__SinceV_Fn)(...) NO_EXCEPTION; +// +// // Name constant for lookup: +// static const char* const kOrtExperimental__SinceV_FnName = "_SinceV"; +// +// Example: ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtApi_ExperimentalApiTest, _Out_ int64_t* out) produces: +// typedef OrtStatusPtr(ORT_API_CALL* OrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_Fn)( +// _Out_ int64_t* out) NO_EXCEPTION; +// static const char* const kOrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_FnName = +// "OrtApi_ExperimentalApiTest_SinceV28"; +#define ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) \ + typedef RET(ORT_API_CALL* OrtExperimental_##NAME##_SinceV##VER##_Fn)(__VA_ARGS__) NO_EXCEPTION; \ + static const char* const kOrtExperimental_##NAME##_SinceV##VER##_FnName = #NAME "_SinceV" #VER; + +#include "onnxruntime_experimental_c_api.inc" + +#undef ORT_EXPERIMENTAL_API diff --git a/include/onnxruntime/core/session/onnxruntime_experimental_c_api_fns.h b/include/onnxruntime/core/session/onnxruntime_experimental_c_api_fns.h deleted file mode 100644 index 43908d2c57721..0000000000000 --- a/include/onnxruntime/core/session/onnxruntime_experimental_c_api_fns.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -// Experimental C API function declarations (per-function plumbing). -// -// This is an internal detail header for onnxruntime_experimental_c_api.h. Do NOT include it directly (enforced via the -// #error below) — include onnxruntime_experimental_c_api.h instead. It must be included after onnxruntime_c_api.h (for -// ORT_API_CALL / NO_EXCEPTION) and after any auxiliary type declarations that the experimental functions reference -// (e.g. ORT_RUNTIME_CLASS(...)). -// -// It performs an X-macro pass over onnxruntime_experimental_c_api.inc to declare, for each experimental function, its -// C function pointer typedef and lookup name constant. - -#pragma once - -#ifndef ORT_INCLUDING_EXPERIMENTAL_C_API_FNS -#error "Include onnxruntime_experimental_c_api.h; do not include this detail header directly." -#endif - -// For each ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) entry in the .inc file, this generates: -// -// // Function pointer typedef: -// typedef RET(ORT_API_CALL* OrtExperimental__SinceV_Fn)(...) NO_EXCEPTION; -// -// // Name constant for lookup: -// static const char* const kOrtExperimental__SinceV_FnName = "_SinceV"; -// -// Example: ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtApi_ExperimentalApiTest, _Out_ int64_t* out) produces: -// typedef OrtStatusPtr(ORT_API_CALL* OrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_Fn)( -// _Out_ int64_t* out) NO_EXCEPTION; -// static const char* const kOrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_FnName = -// "OrtApi_ExperimentalApiTest_SinceV28"; -#define ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) \ - typedef RET(ORT_API_CALL* OrtExperimental_##NAME##_SinceV##VER##_Fn)(__VA_ARGS__) NO_EXCEPTION; \ - static const char* const kOrtExperimental_##NAME##_SinceV##VER##_FnName = #NAME "_SinceV" #VER; - -#include "onnxruntime_experimental_c_api.inc" - -#undef ORT_EXPERIMENTAL_API diff --git a/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h b/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h index c35a7b583997b..030e33fe27b49 100644 --- a/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h +++ b/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h @@ -3,13 +3,10 @@ // Experimental C++ API consumer header. // -// This header provides typed inline accessors in the Ort::Experimental namespace for experimental ORT functions. It is -// the C++ companion to onnxruntime_experimental_c_api.h, which provides the underlying C function pointer typedefs and -// name constants. +// This header provides typed inline accessors in the Ort::Experimental namespace for experimental ORT API functions, +// and any C++ wrapper types associated with the experimental API functions. // -// The per-function accessors are produced from onnxruntime_experimental_c_api.inc (which defines the list of -// experimental API functions) by the detail header onnxruntime_experimental_cxx_api_fns.h, which this header includes -// inside the Ort::Experimental namespace. Hand-written C++ helpers/wrappers live directly in this header. +// It is the C++ companion to onnxruntime_experimental_c_api.h. // // IMPORTANT: Experimental functions are NOT part of the stable ABI. They may be added, changed, or removed between // releases without notice. @@ -37,14 +34,77 @@ #include "onnxruntime_experimental_c_api.h" #include "onnxruntime_cxx_api.h" +namespace Ort { +namespace Experimental { + +// +// Nullable typed accessors +// + +// For each .inc entry, this generates a typed accessor in Ort::Experimental: +// +// inline OrtExperimental__SinceV_Fn Get__SinceV_Fn(const OrtApi* api); +// +// Example: ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtApi_ExperimentalApiTest, ...) produces: +// inline OrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_Fn +// Get_OrtApi_ExperimentalApiTest_SinceV28_Fn(const OrtApi* api) { +// return reinterpret_cast( +// api->GetExperimentalFunction(kOrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_FnName)); +// } +#define ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) \ + inline OrtExperimental_##NAME##_SinceV##VER##_Fn Get_##NAME##_SinceV##VER##_Fn( \ + const OrtApi* api) { \ + return reinterpret_cast( \ + api->GetExperimentalFunction(kOrtExperimental_##NAME##_SinceV##VER##_FnName)); \ + } + +#include "onnxruntime_experimental_c_api.inc" + +#undef ORT_EXPERIMENTAL_API + +// +// Throwing typed accessors +// + +// For each .inc entry, this generates a throwing accessor in Ort::Experimental: +// +// inline OrtExperimental__SinceV_Fn Get__SinceV_FnOrThrow(const OrtApi* api); +// +// It returns the non-null typed function pointer, or throws Ort::Exception (ORT_NOT_IMPLEMENTED) if the function is +// not available in this build. +// +// Example: ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtApi_ExperimentalApiTest, ...) produces: +// inline OrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_Fn +// Get_OrtApi_ExperimentalApiTest_SinceV28_FnOrThrow(const OrtApi* api) { +// auto* fn = Get_OrtApi_ExperimentalApiTest_SinceV28_Fn(api); +// if (fn == nullptr) { +// ORT_CXX_API_THROW( +// "Experimental function OrtApi_ExperimentalApiTest_SinceV28 is not available in this build", +// ORT_NOT_IMPLEMENTED); +// } +// return fn; +// } +#define ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) \ + inline OrtExperimental_##NAME##_SinceV##VER##_Fn Get_##NAME##_SinceV##VER##_FnOrThrow( \ + const OrtApi* api) { \ + auto* fn = Get_##NAME##_SinceV##VER##_Fn(api); \ + if (fn == nullptr) { \ + ORT_CXX_API_THROW( \ + "Experimental function " #NAME "_SinceV" #VER " is not available in this build", \ + ORT_NOT_IMPLEMENTED); \ + } \ + return fn; \ + } + +#include "onnxruntime_experimental_c_api.inc" + +#undef ORT_EXPERIMENTAL_API + +// +// Auxiliary types and helpers // -// Typed accessors (nullable and throwing) +// If C++ wrapper types or helpers are needed in the future, add them here in the `Ort::Experimental` namespace. // -// Generated from onnxruntime_experimental_c_api.inc and declared in the Ort::Experimental namespace. Kept in a separate -// detail header so this header stays focused on the curated, hand-written surface. Custom C++ helpers can be added -// below in their own `namespace Ort { namespace Experimental { ... } }` block. The define/undef guard enforces that the -// detail header is only ever pulled in through this header. -#define ORT_INCLUDING_EXPERIMENTAL_CXX_API_FNS -#include "onnxruntime_experimental_cxx_api_fns.h" -#undef ORT_INCLUDING_EXPERIMENTAL_CXX_API_FNS +} // namespace Experimental +} // namespace Ort diff --git a/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api_fns.h b/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api_fns.h deleted file mode 100644 index bfcc58cc54b7c..0000000000000 --- a/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api_fns.h +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -// Experimental C++ API accessor declarations (per-function plumbing). -// -// This is an internal detail header for onnxruntime_experimental_cxx_api.h. Do NOT include it directly (enforced via -// the #error below) — include onnxruntime_experimental_cxx_api.h instead. It must be included after -// onnxruntime_experimental_c_api.h (for the C typedefs / name constants) and onnxruntime_cxx_api.h (for Ort::Exception -// / ORT_CXX_API_THROW). -// -// It performs two X-macro passes over onnxruntime_experimental_c_api.inc to declare, for each experimental function, -// a nullable typed accessor and a throwing (FnOrThrow) typed accessor, both in the Ort::Experimental namespace. - -#pragma once - -#ifndef ORT_INCLUDING_EXPERIMENTAL_CXX_API_FNS -#error "Include onnxruntime_experimental_cxx_api.h; do not include this detail header directly." -#endif - -namespace Ort { -namespace Experimental { - -// -// Nullable typed accessors -// - -// For each .inc entry, this generates a typed accessor in Ort::Experimental: -// -// inline OrtExperimental__SinceV_Fn Get__SinceV_Fn(const OrtApi* api); -// -// Example: ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtApi_ExperimentalApiTest, ...) produces: -// inline OrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_Fn -// Get_OrtApi_ExperimentalApiTest_SinceV28_Fn(const OrtApi* api) { -// return reinterpret_cast( -// api->GetExperimentalFunction(kOrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_FnName)); -// } -#define ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) \ - inline OrtExperimental_##NAME##_SinceV##VER##_Fn Get_##NAME##_SinceV##VER##_Fn( \ - const OrtApi* api) { \ - return reinterpret_cast( \ - api->GetExperimentalFunction(kOrtExperimental_##NAME##_SinceV##VER##_FnName)); \ - } - -#include "onnxruntime_experimental_c_api.inc" - -#undef ORT_EXPERIMENTAL_API - -// -// Throwing typed accessors -// - -// For each .inc entry, this generates a throwing accessor in Ort::Experimental: -// -// inline OrtExperimental__SinceV_Fn Get__SinceV_FnOrThrow(const OrtApi* api); -// -// It returns the non-null typed function pointer, or throws Ort::Exception (ORT_NOT_IMPLEMENTED) if the function is -// not available in this build. -// -// Example: ORT_EXPERIMENTAL_API(28, OrtStatusPtr, OrtApi_ExperimentalApiTest, ...) produces: -// inline OrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_Fn -// Get_OrtApi_ExperimentalApiTest_SinceV28_FnOrThrow(const OrtApi* api) { -// auto* fn = Get_OrtApi_ExperimentalApiTest_SinceV28_Fn(api); -// if (fn == nullptr) { -// ORT_CXX_API_THROW( -// "Experimental function OrtApi_ExperimentalApiTest_SinceV28 is not available in this build", -// ORT_NOT_IMPLEMENTED); -// } -// return fn; -// } -#define ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) \ - inline OrtExperimental_##NAME##_SinceV##VER##_Fn Get_##NAME##_SinceV##VER##_FnOrThrow( \ - const OrtApi* api) { \ - auto* fn = Get_##NAME##_SinceV##VER##_Fn(api); \ - if (fn == nullptr) { \ - ORT_CXX_API_THROW( \ - "Experimental function " #NAME "_SinceV" #VER " is not available in this build", \ - ORT_NOT_IMPLEMENTED); \ - } \ - return fn; \ - } - -#include "onnxruntime_experimental_c_api.inc" - -#undef ORT_EXPERIMENTAL_API - -} // namespace Experimental -} // namespace Ort From ba1b5251d473df81b4f128fc9b14ca84212b1a3d Mon Sep 17 00:00:00 2001 From: edgchen1 <18449977+edgchen1@users.noreply.github.com> Date: Wed, 17 Jun 2026 11:53:47 -0700 Subject: [PATCH 3/4] update warning in cxx header --- .../onnxruntime/core/session/onnxruntime_experimental_cxx_api.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h b/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h index 030e33fe27b49..5ce03e1ca01dd 100644 --- a/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h +++ b/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h @@ -9,7 +9,7 @@ // It is the C++ companion to onnxruntime_experimental_c_api.h. // // IMPORTANT: Experimental functions are NOT part of the stable ABI. They may be added, changed, or removed between -// releases without notice. +// releases without notice. Anything in this file should be treated as experimental and unstable. // // Two accessor flavors are generated for each experimental function: // From 7e319471378cd7246042b2c54c4a10e6f5f66399 Mon Sep 17 00:00:00 2001 From: edgchen1 <18449977+edgchen1@users.noreply.github.com> Date: Wed, 17 Jun 2026 12:13:29 -0700 Subject: [PATCH 4/4] update header comments --- .../core/session/onnxruntime_experimental_c_api.h | 2 +- .../session/onnxruntime_experimental_cxx_api.h | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/include/onnxruntime/core/session/onnxruntime_experimental_c_api.h b/include/onnxruntime/core/session/onnxruntime_experimental_c_api.h index 191928b88ca88..e5b9bd4713a1c 100644 --- a/include/onnxruntime/core/session/onnxruntime_experimental_c_api.h +++ b/include/onnxruntime/core/session/onnxruntime_experimental_c_api.h @@ -47,7 +47,7 @@ ORT_RUNTIME_CLASS(ModelPackageContext); ORT_RUNTIME_CLASS(ModelPackageComponentContext); // -// C: function pointer typedefs and name constants +// C function pointer typedefs and name constants // // For each ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) entry in the .inc file, this generates: diff --git a/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h b/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h index 5ce03e1ca01dd..fbd7ba3659435 100644 --- a/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h +++ b/include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h @@ -13,12 +13,14 @@ // // Two accessor flavors are generated for each experimental function: // -// 1. Get__SinceV_Fn(api) -> returns the typed function pointer, or nullptr if the function is not -// available in this build. Use this to check availability at runtime. +// 1. Get__SinceV_Fn(api) +// Returns the typed function pointer, or nullptr if the function is not available in this build. +// Use this to check availability at runtime. // -// 2. Get__SinceV_FnOrThrow(api) -> returns the typed function pointer, or throws Ort::Exception -// (ORT_NOT_IMPLEMENTED) if the function is not available in this build. -// Use this when the function is required. +// 2. Get__SinceV_FnOrThrow(api) +// Returns the typed function pointer, or throws Ort::Exception (ORT_NOT_IMPLEMENTED) if the function is not +// available in this build. +// Use this when the function is required. // // C++ usage (nullable): // if (auto* fn = Ort::Experimental::Get_OrtApi_ExperimentalApiTest_SinceV28_Fn(api)) { @@ -103,7 +105,7 @@ namespace Experimental { // // Auxiliary types and helpers // -// If C++ wrapper types or helpers are needed in the future, add them here in the `Ort::Experimental` namespace. +// C++ wrapper types or helpers go here in the `Ort::Experimental` namespace. // } // namespace Experimental