多年前我写过编译期字符串Hash和再探编译期字符串Hash两篇博文,分别证明了C++98下无法实现编译期的字符串hash,以及如何在C++11下用constexpr实现。过了这么多年,原有的实现在Clang上出现了严重的编译性能下降,需要一些修改才能顺利编译。而vc14也开始支持constexpr了,经过实验,发现问题仍很严重。所以这里不得不再次试着改进编译期字符串hash的方法。
旧方法回顾
上次的实现用了constexpr配合模板嵌套,实现了一个初步的编译期计算字符串hash的方法。
constexpr size_t _Hash(const char (&str)[1])
{
return *str + 0x9e3779b9;
}
template <size_t N>
constexpr size_t _Hash(const char (&str)[N])
{
...
时间过得真快,离上一次讨论编译期字符串Hash已经差不多2年了。上次的尝试失败了,只能产生优化期常量,没有做到编译期常量的效果。现在有了C++11,情况会不会有所改变呢?
constexpr
上次的帖子中,在回复mtlung的时候,我提到了C++0x的constexpr可能会成为救星。现在C++11的标准出来了,GCC 4.6+和Clang 3.1+也开始支持constexpr了。首先尝试宏的做法,代码和上次几乎相同:
constexpr inline size_t HASH_FUNCTION(size_t seed, char ch)
{
return seed ^ (ch + 0x9e3779b9 + (seed << 6) + (seed >> 2));
}
#define HASH_RECURSE_00(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECUR ...
老早就想写这篇了,但一直没空,现在抽个空写写吧。
Motivation
编译期字符串Hash的想法源于一个常见做法,字符串比较。比如在KlayGE的effect系统中,有很多if ("cull_mode" == state_name)这样的比较。加速这个操作的方法之一就是hash。但是如果改成if (0x8F1E5E5F == state_name_hash)又非常不直观。如果能有一个编译期的hash方法,先称之为CT_HASH,就可以if(CT_HASH("cull_mode") == state_name_hash),既保证直观,又保证开发效率。
尝试
要在编译期做事情首先都会想到template,但是C++的template没法提供字符串的操作,就算boost::mpl::string,也必须把一个一个字符分开塞给它才行,就像这样:
boost::mpl::string<'c', 'u', 'l', ...