Skip to content

Commit 63cba4b

Browse files
jnthntatumcopybara-github
authored andcommitted
Add migration guide for CelValue -> cel::Value.
Closes #1520 PiperOrigin-RevId: 948465671
1 parent 24e82b6 commit 63cba4b

1 file changed

Lines changed: 241 additions & 0 deletions

File tree

eval/README.md

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,246 @@
22

33
A C++ implementation of a [Common Expression Language][1] evaluator.
44

5+
## Migrating to cel::Value APIs
6+
7+
New users should prefer using the `cel::Value` APIs ("Modern"). The
8+
`google::api::expr::runtime::CelValue` APIs ("Legacy") are not formally
9+
deprecated at this time, but they now incur some overhead and will not be
10+
updated to support all new features. Internally, both flavors use the same
11+
underlying implementation based on the cel::Value representation.
12+
13+
### When to Migrate
14+
15+
If your integration is stable and you don't need access to newer features there
16+
is no need to migrate at this time.
17+
18+
If you need to add support for optionals or other custom opaque or struct types
19+
you must migrate.
20+
21+
If you need to interactively evaluate from source, you should migrate to avoid
22+
extra proto serialization costs.
23+
24+
### How to Migrate
25+
26+
#### 1. Header, Namespace, and Build Target Mapping
27+
28+
Concept | Legacy API (`google::api::expr::runtime`) | Modern API (`cel`)
29+
:----------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----------------
30+
**Namespace** | `google::api::expr::runtime` | `cel`
31+
**Runtime & Program Creation** | `#include "eval/public/cel_expression.h"`<br>`#include "eval/public/cel_expr_builder_factory.h"`<br>Target: `//eval/public:cel_expression` | `#include "runtime/runtime.h"`<br>`#include "runtime/standard_runtime_builder_factory.h"`<br>Targets: `//runtime`, `//runtime:standard_runtime_builder_factory`
32+
**Value Representation** | `#include "eval/public/cel_value.h"`<br>Target: `//eval/public:cel_value` | `#include "common/value.h"`<br>Target: `//common:value`
33+
**Activation & Variables** | `#include "eval/public/activation.h"`<br>Target: `//eval/public:activation` | `#include "runtime/activation.h"`<br>Target: `//runtime:activation`
34+
**Proto Activation Binding** | `#include "eval/public/activation_bind_helper.h"`<br>Target: `//eval/public:activation_bind_helper` | `#include "runtime/bind_proto_to_activation.h"`<br>Target: `//runtime:bind_proto_to_activation`
35+
**Function Adapters** | `#include "eval/public/cel_function_adapter.h"` | `#include "runtime/function_adapter.h"`
36+
**Interoperability Adapters** | N/A | `#include "common/legacy_value.h"`<br>Target: `//common:legacy_value`
37+
38+
#### 2. Runtime Creation and AST Planning (`CelExpressionBuilder` vs `cel::Runtime`)
39+
40+
In the legacy API, `CelExpressionBuilder` created expression plans from
41+
`google::api::expr::CheckedExpr` protobufs. In the modern API, `cel::Runtime`
42+
creates reusable `cel::Program` instances directly from `cel::Ast` objects
43+
without requiring protobuf serialization round-trips.
44+
45+
**Legacy (`google::api::expr::runtime::CelExpressionBuilder`)**:
46+
47+
```cpp
48+
using ::google::api::expr::runtime::CelExpressionBuilder;
49+
using ::google::api::expr::runtime::CreateCelExpressionBuilder;
50+
using ::google::api::expr::runtime::InterpreterOptions;
51+
using ::google::api::expr::runtime::RegisterBuiltinFunctions;
52+
53+
InterpreterOptions options;
54+
std::unique_ptr<CelExpressionBuilder> builder =
55+
CreateCelExpressionBuilder(descriptor_pool, message_factory, options);
56+
CEL_RETURN_IF_ERROR(RegisterBuiltinFunctions(builder->GetRegistry(), options));
57+
58+
// Requires serializing or passing a google::api::expr::CheckedExpr proto
59+
CEL_ASSIGN_OR_RETURN(std::unique_ptr<CelExpression> plan,
60+
builder->CreateExpression(&checked_expr_proto));
61+
```
62+
63+
**Modern (`cel::Runtime`)**:
64+
65+
```cpp
66+
#include "runtime/runtime.h"
67+
#include "runtime/runtime_options.h"
68+
#include "runtime/standard_runtime_builder_factory.h"
69+
70+
cel::RuntimeOptions options;
71+
CEL_ASSIGN_OR_RETURN(
72+
cel::RuntimeBuilder runtime_builder,
73+
cel::CreateStandardRuntimeBuilder(descriptor_pool, options));
74+
CEL_ASSIGN_OR_RETURN(std::unique_ptr<cel::Runtime> runtime,
75+
std::move(runtime_builder).Build());
76+
77+
// Create program directly from a cel::Ast (e.g., from compiler->Compile(expr))
78+
CEL_ASSIGN_OR_RETURN(std::unique_ptr<cel::Program> program,
79+
runtime->CreateProgram(std::move(ast)));
80+
```
81+
82+
*Note on AST Conversion*: If your architecture persists or receives serialized
83+
`google::api::expr::CheckedExpr` protobufs, convert them to `cel::Ast` using
84+
`cel::CreateAstFromCheckedExpr(checked_expr_proto)` (`#include
85+
"common/ast_proto.h"`, target
86+
`//common:ast_proto`).
87+
88+
#### 3. Creating, Inspecting, and Unpacking Values (`CelValue` vs `cel::Value`)
89+
90+
**Legacy (`google::api::expr::runtime::CelValue`)**:
91+
92+
```cpp
93+
#include "eval/public/cel_value.h"
94+
95+
using ::google::api::expr::runtime::CelValue;
96+
97+
// Creation
98+
CelValue bool_val = CelValue::CreateBool(true);
99+
CelValue int_val = CelValue::CreateInt64(42);
100+
CelValue str_val = CelValue::CreateStringView("hello");
101+
102+
// Inspection & Unpacking
103+
if (bool_val.IsBool()) {
104+
bool b;
105+
bool_val.GetValue(&b);
106+
}
107+
```
108+
109+
**Modern (`cel::Value`)**:
110+
111+
```cpp
112+
#include "common/value.h"
113+
114+
// Creation
115+
cel::Value bool_val = cel::BoolValue(true);
116+
cel::Value int_val = cel::IntValue(42);
117+
cel::Value str_val = cel::StringValue("hello");
118+
119+
// Inspection & Unpacking
120+
if (bool_val.IsBool()) {
121+
bool b = bool_val.GetBool().NativeValue();
122+
} else if (str_val.IsString()) {
123+
std::string s = str_val.GetString().ToString();
124+
} else if (str_val.IsError()) {
125+
absl::Status status = str_val.GetError().ToStatus();
126+
}
127+
```
128+
129+
#### 4. Activation and Protobuf Binding (`BindProtoToActivation`)
130+
131+
The modern equivalent of `activation_bind_helper.h` is
132+
`runtime/bind_proto_to_activation.h`.
133+
134+
**Legacy**:
135+
136+
```cpp
137+
#include "eval/public/activation.h"
138+
#include "eval/public/activation_bind_helper.h"
139+
140+
using ::google::api::expr::runtime::Activation;
141+
using ::google::api::expr::runtime::BindProtoToActivation;
142+
using ::google::api::expr::runtime::ProtoUnsetFieldOptions;
143+
144+
Activation activation;
145+
activation.InsertValue("bool_var", CelValue::CreateBool(true));
146+
CEL_RETURN_IF_ERROR(BindProtoToActivation(
147+
&context_message, &arena, &activation, ProtoUnsetFieldOptions::kBindDefault));
148+
```
149+
150+
**Modern**:
151+
152+
```cpp
153+
#include "runtime/activation.h"
154+
#include "runtime/bind_proto_to_activation.h"
155+
156+
cel::Activation activation;
157+
activation.InsertOrAssignValue("bool_var", cel::BoolValue(true));
158+
CEL_RETURN_IF_ERROR(cel::BindProtoToActivation(
159+
context_message, cel::BindProtoUnsetFieldBehavior::kBindDefaultValue,
160+
descriptor_pool, message_factory, &arena, &activation));
161+
```
162+
163+
#### 5. Program Evaluation
164+
165+
Notice the parameter order change when calling `Evaluate`: `(&arena,
166+
activation)` instead of `(activation, &arena)`.
167+
168+
**Legacy**:
169+
170+
```cpp
171+
CEL_ASSIGN_OR_RETURN(CelValue result, plan->Evaluate(activation, &arena));
172+
```
173+
174+
**Modern**:
175+
176+
```cpp
177+
CEL_ASSIGN_OR_RETURN(cel::Value result, program->Evaluate(&arena, activation));
178+
```
179+
180+
#### 6. Custom Extension Functions (`FunctionAdapter`)
181+
182+
**Legacy (`google::api::expr::runtime::FunctionAdapter`)**:
183+
184+
```cpp
185+
#include "eval/public/cel_function_adapter.h"
186+
187+
using AdapterT = google::api::expr::runtime::FunctionAdapter<
188+
absl::StatusOr<bool>, const CelMap*, CelValue::StringHolder, CelValue>;
189+
CEL_RETURN_IF_ERROR(AdapterT::CreateAndRegister(
190+
"contains", /*receiver_style=*/true, &ContainsFunc, builder->GetRegistry()));
191+
```
192+
193+
**Modern (`cel::FunctionAdapter` / `cel::{arity}FunctionAdapter`)**:
194+
195+
```cpp
196+
#include "runtime/function_adapter.h"
197+
198+
using AdapterT = cel::TernaryFunctionAdapter<
199+
absl::StatusOr<bool>, const cel::MapValue&, const cel::StringValue&, const cel::Value&>;
200+
CEL_RETURN_IF_ERROR(AdapterT::RegisterMemberOverload(
201+
"contains", &ContainsFunc, runtime_builder.function_registry()));
202+
```
203+
204+
### Other behavior notes
205+
206+
There is limited support for interoperability when migrating incrementally. You
207+
can use `#include "common/legacy_value.h"` (build target
208+
`//common:legacy_value`) to adapt compatible value types
209+
across legacy and modern boundaries:
210+
211+
* **Adapting Legacy `CelValue` to Modern `cel::Value`**:
212+
213+
```cpp
214+
#include "common/legacy_value.h"
215+
216+
CEL_ASSIGN_OR_RETURN(cel::Value modern_val,
217+
cel::ModernValue(&arena, legacy_cel_val));
218+
```
219+
220+
* **Adapting Modern `cel::Value` to Legacy `CelValue`**:
221+
222+
```cpp
223+
#include "common/legacy_value.h"
224+
225+
CEL_ASSIGN_OR_RETURN(google::api::expr::runtime::CelValue legacy_val,
226+
cel::LegacyValue(&arena, modern_val));
227+
```
228+
229+
Legacy style extension functions use the interop helpers implicitly and can be
230+
used with modern APIs. `google::api::expr::runtime::CelFunction` is also a
231+
`cel::Function`.
232+
233+
```cpp
234+
std::unique_ptr<google::api::expr::runtime::CelFunction> ext_func = ...;
235+
236+
cel::FunctionDescriptor descriptor = ext_func->descriptor();
237+
CEL_RETURN_IF_ERROR(
238+
runtime_builder.function_registry().Register(
239+
descriptor, std::move(ext_func)));
240+
```
241+
242+
Important: The interop code does not support adapting new types introduced with
243+
`cel::Value` to `google::api::expr::runtime::CelValue`. This means you should
244+
not use any of the legacy APIs if your expressions refer to values with an
245+
optional, custom (non-protobuf) struct, or custom opaque type.
5246

6247
[1]: https://github.com/cel-expr/cel-spec

0 commit comments

Comments
 (0)