使用 ExprTK 多次运行和编译一个简单方程时,我遇到分段错误或地址边界错误。令人惊讶的是,当我排除
exprtk::collect_variables()
函数时,问题就消失了,这没有任何意义,因为该函数只是应该为我提供一个包含方程中每个变量的列表。
#include "exprtk.h"
#include <iostream>
typedef exprtk::symbol_table<float> symbol_table_t;
typedef exprtk::expression<float> expression_t;
typedef exprtk::parser<float> parser_t;
parser_t parser;
expression_t expression;
symbol_table_t symbol_table;
float a = 5.0f;
std::string equation;
void f()
{
symbol_table.clear();
std::vector<std::string> varList;
exprtk::collect_variables(equation, varList); // DELETE THIS LINE FOR THE SEGFAULT TO GO AWAY
symbol_table.add_variable("a", a);
parser.compile(equation, expression);
std::cout << "value is " << expression.value() << std::endl;
}
int main()
{
equation.reserve(16);
equation = "a";
expression.register_symbol_table(symbol_table);
std::cout << "First call" << std::endl;
f();
std::cout << "Second call" << std::endl;
f();
return 0;
}
我真的需要“collect_variables”功能,如果有人能告诉我发生了什么以及如何修复它,我将不胜感激。
提前致谢。
我尝试排除“symbol_table.clear()”调用,代码也可以工作,但它带来了其他问题。
最初,从 ExprTk 自述文件中,我们在多个地方有以下声明:
The life-time of objects registered with or created from a
specific symbol-table must span at least the lifetime of the
symbol table instance and all compiled expressions which
utilise objects, such as variables, strings, vectors and
functions of that symbol-table, otherwise the result will be
undefined behaviour.
在第二次调用
f()
时,第一行 symbol_table
被清除,这意味着对已嵌入表达式中的变量等的引用现在指向无效内存,对这些节点的任何访问都会导致UB.
当在第二次调用
f()
时调用compile方法时,调用表达式实例的析构函数,此时它会尝试查看它所持有的任何节点是否需要被销毁,同时它会访问它所持有的节点在上一次编译调用期间从符号表中获取,这就是您看到崩溃的原因。
包含或排除对
collect_variables
的调用与该问题无关。
使用 UBSAN 和 ASAN 运行代码,生成的诊断结果将正确指出您正在观察的问题。
为了解决崩溃问题,您所需要做的就是在表达式实例上调用release,然后再调用
symbol_table
上的clear。如下:
void f()
{
expression.release();
symbol_table.clear();
...
....
}