You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于在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上下文,具体做法:

  1. 复用符号表:创建全局/单例的符号表,注册动态查找回调(参考问题2的方法),无需每次手动添加变量。
  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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 06:03:12