问题描述
背景
struct event {
uint16_t id;
uint8_t type;
std::string name;
// many more fields (either string or integer types)
};
一个布尔表达式(存储在字符串中):
name = "xyz" and (id = 10 or type = 5)
我有一个由事件对象(数千个)std::vector<event>
组成的向量,我想检查它们是否满足布尔表达式。
到目前为止我已经实现的事情
- 我实现了一个分词器和一个递归下降解析器来将布尔表达式转换为 AST。
- 将终端存储为
std::pair<std::string,std::string>
,其中第一个值是结构字段名称,第二个是值。例如,std::pair<"name","acbd">
- 我穿过这棵树,每当它看到一个终端树节点时:
bool match(const event& ev,const ast::node& node) {
if (node.first == "name") {
return (ev.name == node.second);
} else if (node.first == "id") {
return (ev.id == std::stoi(node.second));
} else if (node.first == "type") {
return (ev.type == std::stoi(node.second));
} // more else if blocks for each member of event struct
...
}
问题
该结构体包含 10 个成员。我想避免不必要的比较。在最坏的情况下,AST 终端节点(例如 pair<"type","5000">
)可能会导致 10 次比较。
我尝试构建此查找映射:
std::map<std::string,std::size_t> fields;
fields["name"] = offsetof(event,name);
fields["id"] = offsetof(event,id);
fields["type"] = offsetof(event,type);
使用此地图,我可以将 match()
简化为:
bool match(const event& ev,const ast::node& node) {
const auto offset = fields[node.first];
return ((ev + offset) == node.second); // DOESN'T WORK
}
-
我可以使用
(eventObj + offset)
以偏移量访问结构成员。如何将其转换为正确的数据类型以进行比较? -
目前,AST 终端节点中的所有字段值都是
std::string
。如何在标记器或解析步骤期间将其转换为正确的类型?我可以将 AST 节点存储为std::pair<std::string,std::any>
,但仍然需要std::any_cast
的类型信息。
如果我能以某种方式将类型信息存储在字段映射中,这两个问题都可以解决。我不确定如何。
解决方法
构建从字段名称到 std::function<std::function<bool(event const&)>(std::string const&)>
的映射,例如:
lookup["name"]=[](auto str){
return [val=std::move(str)](auto& e){
return e.name==val;
};
};
现在您可以将您的对转换为测试函数。
现在你的
name = "xyz" and (id = 10 or type = 5)
变成
tree(
lookup["name"]("xyz"),std::logical_and<>{},tree(
lookup["id"]("17"),std::logical_or<>{},lookup["type"]("5")
)
);
有成堆的工作。