C 复常数折叠

C 复常数折叠,c,gcc,compiler-construction,compiler-optimization,constantfolding,C,Gcc,Compiler Construction,Compiler Optimization,Constantfolding,gcc似乎对复杂常数折叠有一些限制。以下是一个例子: static inline unsigned int DJBHash(const char *str) { int i; unsigned int hash = 5381; for(i = 0; i < strlen(str); i++) { hash = ((hash << 5) + hash) + str[i]; } return hash; } int f(v

gcc似乎对复杂常数折叠有一些限制。以下是一个例子:

static inline unsigned int DJBHash(const char *str)
{
   int i;
   unsigned int hash = 5381;

   for(i = 0; i < strlen(str); i++)
   {
      hash = ((hash << 5) + hash) + str[i];   
   }

   return hash;
}

int f(void)
{   
    return DJBHash("01234567890123456");
}
静态内联无符号整数DJBHash(const char*str)
{
int i;
无符号整数散列=5381;
对于(i=0;ihash=((hash不是答案,只是另一个数据点

下面的实现更糟糕。GCC4.7.3正确地应用了TCO来将这个实现变成一个循环,但它在编译时的计算结果最多只有“0”

static inline unsigned int DJBHash2(const char *str, unsigned int hash) {
   return *str ? DJBHash2(str + 1, 33 * hash + *str) : hash; }
另一方面,递归版本缩短了7个字节

还有人提到了clang,下面是clang 3.1-O3的结果。它为两个版本的DJBHash生成不同的代码,但字节数相同。有趣的是,它将原始版本的移位和加法转换为乘法。它将两个版本的字符串优化为常量,最多100个字符。最后y、 clang代码比最短的GCC代码短5个字节

也许C++ TMP可以做到这一点。但我不确定。

如果您不介意使用可变字符文字列表而不是字符串文字,则可以:

#include <type_traits>
#include <iostream>

template<unsigned acc, char... values>
struct DJBhash_helper
     : std::integral_constant<unsigned, acc> {};

template<unsigned acc, char head, char... tail>
struct DJBhash_helper<acc, head, tail...>
     : DJBhash_helper<(acc << 5) + acc + head, tail...> {};

template<char... str>
struct DJBhash
     : DJBhash_helper<5381, str...> {};

int main()
{
    std::cout << DJBhash<'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                         '0', '1', '2', '3', '4', '5', '6', '7'>::value << '\n';
}
#包括
#包括
模板
结构DJBhash_辅助程序
:std::积分_常数{};
模板

struct DJBhash_helper

这是一个使用
constexpr
的版本。它在一个方面与其他版本稍有不同——可以说是递归的,最容易将字符串前后散列。例如,它为“abc”提供的值将是您通常从“cba”中所期望的值相反,我不认为这在使用上会有任何真正的区别,只要你能始终如一地使用其中一种(但是考虑到散列的变幻莫测性,我可能错了)

但它在编译时进行计算——例如,我们可以在
开关
语句中使用结果作为标签:

#include <iostream>

unsigned constexpr const_hash(char const *input) {
    return *input ?
           static_cast<unsigned>(*input) + 33 * const_hash(input + 1) :
           5381;
}

int main(int argc, char **argv) {
    switch (const_hash(argv[1])) {
    case const_hash("one"): std::cout << "one"; break;
    case const_hash("two"): std::cout << "two"; break;
    }
}
#包括
无符号常量表达式常量散列(字符常量*输入){
返回*输入?
静态强制转换(*输入)+33*常量散列(输入+1):
5381;
}
int main(int argc,字符**argv){
开关(常量散列(argv[1])){

案例STOSH(一):STD::CUT< P> OP对C中的常数折叠感兴趣,但对于C++的兄弟姐妹来说:在C++ 14中,可以简单的将<代码> CONTXPRP</代码>放在两个函数前面,并修改循环以补偿<代码> STRULN()/<代码>不是代码> CONTXPRP</代码> < /P>
#include<iostream>

static inline constexpr unsigned int DJBHash(const char *str)
{
   unsigned int hash = 5381;

   for(auto i = 0; i < 512; ++i) {
      if (*str == '\0') return hash;
      hash = ((hash << 5) + hash) + static_cast<unsigned int>(*str);   
   }

   return hash;
}

constexpr unsigned int f(void)
{   
    return DJBHash("01234567890123456");
}

int main()
{
    constexpr auto h = f(); 
    std::cout << std::hex << h << "\n"; // 88a7b505
}
#包括
静态内联constexpr无符号int-DJBHash(const-char*str)
{
无符号整数散列=5381;
用于(自动i=0;i<512;++i){
if(*str=='\0')返回哈希;

散列=((散列在GCC中可能有一个选项,允许您设置一些阈值,以便决定展开某些内容。持续展开可能是一个无休止的过程。此外,决定展开是否会终止可能是一个暂停问题。@Mystical我查看了这个选项,但找不到与持续展开相关的任何内容折叠。我试着改变循环展开参数<代码> max un滚动时间< /C>。但是这似乎没有任何效果。那么你可以做的事情不多。你只能依靠编译器来做。这绝对是在推动边界。在某个时候你需要明确。也许C++ TMP可能能做到。我不确定。在C++中,你可以在编译时用<代码> CONTXPRPR < /COD> BTW计算,优化器可以转换<代码> 33 *哈希< /C> >自己移动和添加。你可以使用<代码> Boo::MPL::字符串< /代码>。你可以得到一个使用尾递归辅助函数的正确结果,看我的答案。@ FredOverflow:是的,你可以,但是我不在。e它有足够的差异来证明额外的工作/代码。为什么会有第一个
静态\u cast
?尾部递归版本不需要帮助函数。它看起来非常像@AdamBurry的
DJBHash2
,但是
哈希
应该有默认值
=5381
。这是这个版本更好的原因。W当您在运行时使用
const_hash
时,它将运行得更快,并且不会占用堆栈。请注意,此代码的保存期限很短:只要C++14
constexpr
(已在Clang 3.4 SVN中提供)如果更广泛地使用,您可以使用我的答案。在constexpr的情况下,Clang如何计算最终值?通过直接解释AST或jit编译函数?@Stringer前者,语言保证在启动程序之前对
h
进行评估对不起,但是在constexpr上内联是多余的,因为它暗示着这一点。
#include<iostream>

static inline constexpr unsigned int DJBHash(const char *str)
{
   unsigned int hash = 5381;

   for(auto i = 0; i < 512; ++i) {
      if (*str == '\0') return hash;
      hash = ((hash << 5) + hash) + static_cast<unsigned int>(*str);   
   }

   return hash;
}

constexpr unsigned int f(void)
{   
    return DJBHash("01234567890123456");
}

int main()
{
    constexpr auto h = f(); 
    std::cout << std::hex << h << "\n"; // 88a7b505
}