关于在C++开源智能家居应用中集成ExprTk的动态变量查询问题
ExprTk集成C++智能家居应用的问题解答
我正在将ExprTk集成到自研的C++开源智能家居应用中,该应用包含开关状态(BOOL)、温度值(double)等实时动态变化的变量,且已维护自行管理的实时更新符号表。希望利用这些变量评估如OUTSIDE_TEMP > 60或TODAY_MINS > 3600 && RELAY_1 == TRUE这类表达式,现存在以下疑问:
问题1:每次调用parser.compile()时是否必须指定所有变量名?能否在expression.value()阶段动态查找变量值并完成评估?
不需要每次编译都指定所有变量。ExprTk支持动态符号解析,你可以通过symbol_table的回调机制,在表达式求值(value()阶段)时动态查找变量值。
具体可以用symbol_table_t::set_on_unknown_symbol注册回调,当ExprTk遇到未预先定义的符号时,自动调用该回调从你的实时符号表中获取对应值,无需在编译阶段提前添加所有变量。
问题2:此类场景的规范实现方式是什么?是否需要创建LOOKUP函数,通过传入变量名字符串(如LOOKUP("OUTSIDE_TEMP") > 60)来实现?
规范实现是利用ExprTk的动态符号回调机制,不用额外写LOOKUP函数——后者会让表达式冗余且不直观。
注册未知符号处理回调的示例思路:
symbol_table_t sym_table; sym_table.set_on_unknown_symbol([this](const std::string& name, double& value) -> bool { // 从你的应用实时符号表中查询变量值 if (my_app_symbol_table.has_variable(name)) { value = my_app_symbol_table.get_variable_value(name); return true; } return false; // 找不到则返回错误 });
这样表达式可以直接写OUTSIDE_TEMP > 60,无需额外包装。
问题3:是否可通过不定义任何符号直接编译,从错误信息中提取所需符号?另外,ExprTk上下文能否序列化以便后续复用?
- 提取未定义符号:可以。编译时若存在未定义符号,
parser.error()会包含相关信息,你可以解析错误消息提取出所有未定义变量名,再动态注册后重新编译。但这种方式繁琐,不如直接用动态符号回调高效。 - 序列化ExprTk上下文:ExprTk本身没有内置序列化机制。进程内可以直接缓存编译后的
expression_t对象复用;如果需要跨进程或持久化,只能自行实现——比如保存表达式字符串和所需变量列表,下次使用时重新编译(编译开销不大,适合多数场景)。
问题4:我已实现如下test1代码,但不想每次都指定所有变量,能否保存ExprTk上下文?
void test1() { using T = double; typedef exprtk::symbol_table<T> symbol_table_t; typedef exprtk::expression<T> expression_t; typedef exprtk::parser<T> parser_t; symbol_table_t immutable_symbol_table( symbol_table_t::symtab_mutability_type::e_immutable); double outside_temp = -4; immutable_symbol_table.add_variable("OUTSIDE_TEMP" , outside_temp ); expression_t expression; expression.register_symbol_table(immutable_symbol_table); parser_t parser; if (!parser.compile("OUTSIDE_TEMP > 60.0", expression)) { cout << "Error: " << parser.error() << endl; } else { outside_temp = 99.0; cout << expression.value() <<endl; } }
可以保存ExprTk上下文,具体做法:
- 复用符号表:创建全局/单例的符号表,注册动态查找回调(参考问题2的方法),无需每次手动添加变量。
- 缓存已编译表达式:用容器(比如
std::unordered_map<std::string, expression_t>)存储表达式字符串和对应的编译结果,后续直接调用value()求值即可,无需重新编译。
注意:如果变量值会实时变化,不要用e_immutable类型的符号表,改用默认可变符号表,确保动态回调能获取最新值。修改后的示例思路:
// 全局/单例符号表,注册动态回调 symbol_table_t global_sym_table; global_sym_table.set_on_unknown_symbol([](const std::string& name, double& val) -> bool { // 从你的应用符号表获取最新值 val = get_app_real_time_variable(name); return true; }); // 编译一次,缓存表达式 expression_t cached_expr; cached_expr.register_symbol_table(global_sym_table); parser_t parser; parser.compile("OUTSIDE_TEMP > 60.0", cached_expr); // 后续多次求值,直接调用value() double result = cached_expr.value();
内容的提问来源于stack exchange,提问作者vinthewrench
相关产品推荐
相关产品推荐

