A C++ implementation of a Common Expression Language evaluator.
New users should prefer using the cel::Value APIs ("Modern"). The
google::api::expr::runtime::CelValue APIs ("Legacy") are not formally
deprecated at this time, but they now incur some overhead and will not be
updated to support all new features. Internally, both flavors use the same
underlying implementation based on the cel::Value representation.
If your integration is stable and you don't need access to newer features there is no need to migrate at this time.
If you need to add support for optionals or other custom opaque or struct types you must migrate.
If you need to interactively evaluate from source, you should migrate to avoid extra proto serialization costs.
| Concept | Legacy API (google::api::expr::runtime) |
Modern API (cel) |
|---|---|---|
| Namespace | google::api::expr::runtime |
cel |
| Runtime & Program Creation | #include "eval/public/cel_expression.h"#include "eval/public/cel_expr_builder_factory.h"Target: //eval/public:cel_expression |
#include "runtime/runtime.h"#include "runtime/standard_runtime_builder_factory.h"Targets: //runtime, //runtime:standard_runtime_builder_factory |
| Value Representation | #include "eval/public/cel_value.h"Target: //eval/public:cel_value |
#include "common/value.h"Target: //common:value |
| Activation & Variables | #include "eval/public/activation.h"Target: //eval/public:activation |
#include "runtime/activation.h"Target: //runtime:activation |
| Proto Activation Binding | #include "eval/public/activation_bind_helper.h"Target: //eval/public:activation_bind_helper |
#include "runtime/bind_proto_to_activation.h"Target: //runtime:bind_proto_to_activation |
| Function Adapters | #include "eval/public/cel_function_adapter.h" |
#include "runtime/function_adapter.h" |
| Interoperability Adapters | N/A | #include "common/legacy_value.h"Target: //common:legacy_value |
In the legacy API, CelExpressionBuilder created expression plans from
google::api::expr::CheckedExpr protobufs. In the modern API, cel::Runtime
creates reusable cel::Program instances directly from cel::Ast objects
without requiring protobuf serialization round-trips.
Legacy (google::api::expr::runtime::CelExpressionBuilder):
using ::google::api::expr::runtime::CelExpressionBuilder;
using ::google::api::expr::runtime::CreateCelExpressionBuilder;
using ::google::api::expr::runtime::InterpreterOptions;
using ::google::api::expr::runtime::RegisterBuiltinFunctions;
InterpreterOptions options;
std::unique_ptr<CelExpressionBuilder> builder =
CreateCelExpressionBuilder(descriptor_pool, message_factory, options);
CEL_RETURN_IF_ERROR(RegisterBuiltinFunctions(builder->GetRegistry(), options));
// Requires serializing or passing a google::api::expr::CheckedExpr proto
CEL_ASSIGN_OR_RETURN(std::unique_ptr<CelExpression> plan,
builder->CreateExpression(&checked_expr_proto));Modern (cel::Runtime):
#include "runtime/runtime.h"
#include "runtime/runtime_options.h"
#include "runtime/standard_runtime_builder_factory.h"
cel::RuntimeOptions options;
CEL_ASSIGN_OR_RETURN(
cel::RuntimeBuilder runtime_builder,
cel::CreateStandardRuntimeBuilder(descriptor_pool, options));
CEL_ASSIGN_OR_RETURN(std::unique_ptr<cel::Runtime> runtime,
std::move(runtime_builder).Build());
// Create program directly from a cel::Ast (e.g., from compiler->Compile(expr))
CEL_ASSIGN_OR_RETURN(std::unique_ptr<cel::Program> program,
runtime->CreateProgram(std::move(ast)));Note on AST Conversion: If your architecture persists or receives serialized
google::api::expr::CheckedExpr protobufs, convert them to cel::Ast using
cel::CreateAstFromCheckedExpr(checked_expr_proto) (#include "common/ast_proto.h", target
//common:ast_proto).
Legacy (google::api::expr::runtime::CelValue):
#include "eval/public/cel_value.h"
using ::google::api::expr::runtime::CelValue;
// Creation
CelValue bool_val = CelValue::CreateBool(true);
CelValue int_val = CelValue::CreateInt64(42);
CelValue str_val = CelValue::CreateStringView("hello");
// Inspection & Unpacking
if (bool_val.IsBool()) {
bool b;
bool_val.GetValue(&b);
}Modern (cel::Value):
#include "common/value.h"
// Creation
cel::Value bool_val = cel::BoolValue(true);
cel::Value int_val = cel::IntValue(42);
cel::Value str_val = cel::StringValue("hello");
// Inspection & Unpacking
if (bool_val.IsBool()) {
bool b = bool_val.GetBool().NativeValue();
} else if (str_val.IsString()) {
std::string s = str_val.GetString().ToString();
} else if (str_val.IsError()) {
absl::Status status = str_val.GetError().ToStatus();
}The modern equivalent of activation_bind_helper.h is
runtime/bind_proto_to_activation.h.
Legacy:
#include "eval/public/activation.h"
#include "eval/public/activation_bind_helper.h"
using ::google::api::expr::runtime::Activation;
using ::google::api::expr::runtime::BindProtoToActivation;
using ::google::api::expr::runtime::ProtoUnsetFieldOptions;
Activation activation;
activation.InsertValue("bool_var", CelValue::CreateBool(true));
CEL_RETURN_IF_ERROR(BindProtoToActivation(
&context_message, &arena, &activation, ProtoUnsetFieldOptions::kBindDefault));Modern:
#include "runtime/activation.h"
#include "runtime/bind_proto_to_activation.h"
cel::Activation activation;
activation.InsertOrAssignValue("bool_var", cel::BoolValue(true));
CEL_RETURN_IF_ERROR(cel::BindProtoToActivation(
context_message, cel::BindProtoUnsetFieldBehavior::kBindDefaultValue,
descriptor_pool, message_factory, &arena, &activation));Notice the parameter order change when calling Evaluate: (&arena, activation) instead of (activation, &arena).
Legacy:
CEL_ASSIGN_OR_RETURN(CelValue result, plan->Evaluate(activation, &arena));Modern:
CEL_ASSIGN_OR_RETURN(cel::Value result, program->Evaluate(&arena, activation));Legacy (google::api::expr::runtime::FunctionAdapter):
#include "eval/public/cel_function_adapter.h"
using AdapterT = google::api::expr::runtime::FunctionAdapter<
absl::StatusOr<bool>, const CelMap*, CelValue::StringHolder, CelValue>;
CEL_RETURN_IF_ERROR(AdapterT::CreateAndRegister(
"contains", /*receiver_style=*/true, &ContainsFunc, builder->GetRegistry()));Modern (cel::FunctionAdapter / cel::{arity}FunctionAdapter):
#include "runtime/function_adapter.h"
using AdapterT = cel::TernaryFunctionAdapter<
absl::StatusOr<bool>, const cel::MapValue&, const cel::StringValue&, const cel::Value&>;
CEL_RETURN_IF_ERROR(AdapterT::RegisterMemberOverload(
"contains", &ContainsFunc, runtime_builder.function_registry()));There is limited support for interoperability when migrating incrementally. You
can use #include "common/legacy_value.h" (build target
//common:legacy_value) to adapt compatible value types
across legacy and modern boundaries:
-
Adapting Legacy
CelValueto Moderncel::Value:#include "common/legacy_value.h" CEL_ASSIGN_OR_RETURN(cel::Value modern_val, cel::ModernValue(&arena, legacy_cel_val));
-
Adapting Modern
cel::Valueto LegacyCelValue:#include "common/legacy_value.h" CEL_ASSIGN_OR_RETURN(google::api::expr::runtime::CelValue legacy_val, cel::LegacyValue(&arena, modern_val));
Legacy style extension functions use the interop helpers implicitly and can be
used with modern APIs. google::api::expr::runtime::CelFunction is also a
cel::Function.
std::unique_ptr<google::api::expr::runtime::CelFunction> ext_func = ...;
cel::FunctionDescriptor descriptor = ext_func->descriptor();
CEL_RETURN_IF_ERROR(
runtime_builder.function_registry().Register(
descriptor, std::move(ext_func)));Important: The interop code does not support adapting new types introduced with
cel::Value to google::api::expr::runtime::CelValue. This means you should
not use any of the legacy APIs if your expressions refer to values with an
optional, custom (non-protobuf) struct, or custom opaque type.