Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmake/onnxruntime.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +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_cxx_api.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"
Expand Down
89 changes: 69 additions & 20 deletions docs/design/Experimental_C_API.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,17 +103,27 @@ 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`)

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.
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
// onnxruntime_experimental_c_api.h (public)
#pragma once

#include "onnxruntime_c_api.h"

// Declare any new, auxiliary opaque types required by the experimental APIs in this header too.
// ORT_RUNTIME_CLASS(...);

// --- C: function pointer typedefs and name constants ---
#define ORT_EXPERIMENTAL_API(VER, RET, NAME, ...) \
Expand All @@ -127,35 +137,67 @@ 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

#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<OrtExperimental_##NAME##_SinceV##VER##_Fn>( \
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<OrtExperimental_##NAME##_SinceV##VER##_Fn>( \
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<OrtExperimental_OrtApi_SomeNewThing_SinceV22_Fn>(
// 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:
Expand All @@ -169,14 +211,21 @@ if (fn) {
}
```

C++ usage:
C++ usage (nullable):

```cpp
if (auto* fn = Ort::Experimental::Get_OrtApi_SomeNewThing_SinceV22_Fn(api)) {
Ort::Status status(fn(session, &result));
}
```

C++ usage (throwing):

```cpp
auto* fn = Ort::Experimental::Get_OrtApi_SomeNewThing_SinceV22_FnOrThrow(api);
Ort::Status status(fn(session, &result));
Comment thread
edgchen1 marked this conversation as resolved.
```

### Implementation Side (generated from `.inc`)

```cpp
Expand Down
54 changes: 11 additions & 43 deletions include/onnxruntime/core/session/onnxruntime_experimental_c_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@

// Experimental C API consumer header.
//
// This header provides typedefs, name constants, and (for C++) typed inline accessors 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.
//
// This header contains code generated from onnxruntime_experimental_c_api.inc, which defines the list of experimental
// API functions.
// 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).
//
// 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 =
Expand All @@ -21,7 +24,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));
// }
Expand All @@ -44,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:
Expand All @@ -67,38 +70,3 @@ ORT_RUNTIME_CLASS(ModelPackageComponentContext);
#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_<NAME>_SinceV<VER>_Fn Get_<NAME>_SinceV<VER>_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<OrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_Fn>(
// 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<OrtExperimental_##NAME##_SinceV##VER##_Fn>( \
api->GetExperimentalFunction(kOrtExperimental_##NAME##_SinceV##VER##_FnName)); \
}

#include "onnxruntime_experimental_c_api.inc"

#undef ORT_EXPERIMENTAL_API

} // namespace Experimental
} // namespace Ort

#endif // __cplusplus
112 changes: 112 additions & 0 deletions include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// 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 API functions,
// and any C++ wrapper types associated with the experimental API functions.
//
// 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. Anything in this file should be treated as experimental and unstable.
//
// Two accessor flavors are generated for each experimental function:
//
// 1. Get_<NAME>_SinceV<VER>_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_<NAME>_SinceV<VER>_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"

Check warning on line 36 in include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h

View workflow job for this annotation

GitHub Actions / Optional Lint C++

[cpplint] reported by reviewdog 🐶 Include the directory when naming header files [build/include_subdir] [4] Raw Output: include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h:36: Include the directory when naming header files [build/include_subdir] [4]
#include "onnxruntime_cxx_api.h"

Check warning on line 37 in include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h

View workflow job for this annotation

GitHub Actions / Optional Lint C++

[cpplint] reported by reviewdog 🐶 Include the directory when naming header files [build/include_subdir] [4] Raw Output: include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h:37: Include the directory when naming header files [build/include_subdir] [4]

namespace Ort {
namespace Experimental {

//
// Nullable typed accessors
//

// For each .inc entry, this generates a typed accessor in Ort::Experimental:
//
// inline OrtExperimental_<NAME>_SinceV<VER>_Fn Get_<NAME>_SinceV<VER>_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<OrtExperimental_OrtApi_ExperimentalApiTest_SinceV28_Fn>(
// 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<OrtExperimental_##NAME##_SinceV##VER##_Fn>( \
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_<NAME>_SinceV<VER>_Fn Get_<NAME>_SinceV<VER>_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"

Check warning on line 101 in include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h

View workflow job for this annotation

GitHub Actions / Optional Lint C++

[cpplint] reported by reviewdog 🐶 "onnxruntime_experimental_c_api.inc" already included at include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h:63 [build/include] [4] Raw Output: include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h:101: "onnxruntime_experimental_c_api.inc" already included at include/onnxruntime/core/session/onnxruntime_experimental_cxx_api.h:63 [build/include] [4]

#undef ORT_EXPERIMENTAL_API

//
// Auxiliary types and helpers
//
// C++ wrapper types or helpers go here in the `Ort::Experimental` namespace.
//

} // namespace Experimental
} // namespace Ort
Loading
Loading