我正在用C ++编写一个简单的解析器,它使用字符串'triggers'到'handler'函数指针的映射,我的问题是实现生成和访问的最“静态”和有效的方法。地图吗?
我首先考虑了一种方法,例如Parser::add_handler
,它会将触发器/处理程序添加到解析器的映射中,但据我所知,每次运行程序时都需要执行此操作,而数据在编译时是已知的。 (虽然从正面看,它们只需要执行一次,而不是每个实例化的Parser。)
Parser::get_handlers
将在派生类中实现,以返回该解析器的处理程序映射。这似乎是一个更好的封装解决方案,虽然它需要为每个创建的解析器实例调用虚函数,至少有一次调用解析器的地图生成函数。
目前使用后一种方法似乎更合适,但它仍然会在每次执行时动态生成地图,是否可以避免这种情况?
答案 0 :(得分:1)
如果您不想动态构建地图,可以使用带std::lower_bound
的已排序静态数组在O(log n)时间内搜索它。
如果你有一个好的哈希映射实现,你可能会发现填充它的开销小于运行时的性能增益,这取决于你需要做多少次查找。
答案 1 :(得分:1)
如果您在编译时知道所有字符串及其处理程序并希望在运行时匹配它们,并且如果您没有很多字符串并且它们的长度不是很长,并且可能的范围这些字符串中的字符不是很大(就像在ASCII中一样),那么你可以在编译时使用模板或简单地通过自己的硬编码来构建静态调度表。这种方法也可能导致性能不佳,具体取决于字符串的类型和数量。
或者,每个字符串的长度可以有一个数组,其中键是该字符串中字符的总和(它们的数字表示)。
但我建议您从Google的dense_hash_map
开始。不要花太多时间过早地优化您的软件。程序完成后,如果您对性能不满意,请使用分析器查找瓶颈在哪里并提高性能。
答案 2 :(得分:0)
首先,它看起来有点矫枉过正,你是否想浪费一些时间来优化不需要优化的东西,或者你只是好奇?
它取决于...如果函数名都知道,那么这一切都取决于你拥有的函数的数量。您可以简单地以这种方式创建字符串指针数组,以便对指针进行排序,然后进行手动搜索。如果列表中只有几个函数,则可以进行线性搜索。如果你有很多,那么出于性能原因,手动进行二进制搜索(但必须按排序顺序排列指针)。
但是,对于实际工作,绝不使用std :: map!它是专为您的情况设计的:插入很昂贵,但一旦创建了一次地图,那么搜索速度非常快(二进制搜索)。你会得到干净正确的代码。
答案 3 :(得分:0)
如果你想要超级高效,你可以创建一个parsing tree作为Plain Old数据结构。如果你想要很好地完成这项工作,你可以创建一个程序,根据规范生成解析树(这是lex/flex正在为你做的事情),然后将生成的生成数据结构编译到你的程序中。
然后您不需要add_handler
,因为在编译时添加了所有数据。
话虽如此,对于大多数任务,您可能不需要这种级别的优化,因此我总是建议首先获得正确的功能,然后再考虑如何使用此类方法进行优化。
答案 4 :(得分:0)
根据您的要求,有许多有趣的解决方案 比第二种更快的方法是在具有每个触发器的函数指针成员的类上模板Parser,因此根本没有运行时开销。事实上,它是允许内联的唯一解决方案。它确实有一个限制,即单个类必须拥有所有处理程序。
template<class handler>
struct Parser {
void ParseLines() {
if (lineBeginsWith('+')
handler.lineBeginsPlus();
if (lineBeginsWith('-')
handler.lineBeginsMinus();
}
};
struct LineHandlers {
void lineBeginsPlus() {
printf("handle + line");
}
void lineBeginsMinus() {
printf("handle - line");
}
};
struct OtherLineHandlers {
void lineBeginsPlus() {
printf("handle + line differently");
}
void lineBeginsMinus() {
printf("handle - line differently");
}
};
int main() {
Parser<LineHandlers> ParserInstance;
ParserInstance.ParseLines();
Parser<OtherLineHandlers> OtherParserInstance;
OtherParserInstance.ParseLines();
}