我试图解析用户输入并根据用户给出的命令执行一些任务.因为,在C中,switch不能用于字符串我决定使用字符串哈希值的切换来比较要执行的命令.
现在,维护所有可用命令的所有哈希列表,如下所示
#define EXIT 6385204799 ...
是一个非常繁琐的任务,如果有方法说服gcc
在编译时使用常量参数来评估哈希函数,那么我就是在徘徊,所以我可以使用这样的东西
switch(hash(command)){ case hash("exit"): exit(); // I know, case labels must be compile time constants // but that should be fulfilled in my case }
我知道我可以使用例如元编程,但我对gcc的解决方案更感兴趣.
它甚至可能吗?
#includeunsigned long hash(const char *str) { unsigned long hash = 5381; int c; while ((c = *str++)) hash = ((hash << 5) + hash) + c; return hash; } int main( int argc, char **argv ) { char *command=NULL; size_t size=0; printf("Enter string:"); getline(&command, &size, stdin); printf("%ld",hash("exit")); // I want this to evaluate in compile time printf("%ld",hash(command)); // and this not return 0; }
davmac.. 5
它甚至可能吗?
GCC不能(对于C - 它可以用于C++,见下文),但Clang/LLVM(版本3.9.1)可以.使用此-O2
开关启用2级优化(或更高).
证明.参见反汇编 - 没有调用哈希函数,没有循环; 编译器在编译时计算了哈希值.这种简化形式的测试用例:
#includestatic unsigned long hash(const char *str) { unsigned long hash = 5381; int c; while ((c = *str++)) hash = ((hash << 5) + hash) + c; return hash; } int main( int argc, char **argv ) { size_t size=0; printf("%ld",hash("exit")); // I want this to evaluate in compile time return 0; }
编译为:
main: # @main # BB#0: push rax #DEBUG_VALUE: main:argc <- %EDI #DEBUG_VALUE: main:argv <- %RSI #DEBUG_VALUE: main:size <- 0 movabs rsi, 6385204799 mov edi, .L.str xor eax, eax call printf xor eax, eax pop rcx ret .L.str: .asciz "%ld"
该行movabs rsi, 6385204799
直接将预先计算的哈希值加载到rsi
寄存器中.
但是,为了case
在switch
语句中的标签中使用,该值不会被视为编译时常量.您需要使用if ... else
比较而不是switch
.
如果您感兴趣,使用现代C++,您可以使用GCC和Clang/LLVM实现这种类型的优化,您甚至可以使用以下switch
语句:
#includestatic constexpr unsigned long hash(const char *str) { unsigned long hash = 5381; int c = *str; while ((c = *str++)) hash = ((hash << 5) + hash) + c; return hash; } int main( int argc, char **argv ) { size_t size=0; printf("%ld",hash("exit")); // I want this to evaluate in compile time switch((unsigned long)5 /* i.e. some number */) { case hash("exit"): // etc ; } return 0; }
这是C++ 14代码,您需要使用-std=c++14
它来编译它(或使用GCC 6+,这是默认值).(当然,代码不是惯用的C++ - 它旨在尽可能接近前面的例子).
它甚至可能吗?
GCC不能(对于C - 它可以用于C++,见下文),但Clang/LLVM(版本3.9.1)可以.使用此-O2
开关启用2级优化(或更高).
证明.参见反汇编 - 没有调用哈希函数,没有循环; 编译器在编译时计算了哈希值.这种简化形式的测试用例:
#includestatic unsigned long hash(const char *str) { unsigned long hash = 5381; int c; while ((c = *str++)) hash = ((hash << 5) + hash) + c; return hash; } int main( int argc, char **argv ) { size_t size=0; printf("%ld",hash("exit")); // I want this to evaluate in compile time return 0; }
编译为:
main: # @main # BB#0: push rax #DEBUG_VALUE: main:argc <- %EDI #DEBUG_VALUE: main:argv <- %RSI #DEBUG_VALUE: main:size <- 0 movabs rsi, 6385204799 mov edi, .L.str xor eax, eax call printf xor eax, eax pop rcx ret .L.str: .asciz "%ld"
该行movabs rsi, 6385204799
直接将预先计算的哈希值加载到rsi
寄存器中.
但是,为了case
在switch
语句中的标签中使用,该值不会被视为编译时常量.您需要使用if ... else
比较而不是switch
.
如果您感兴趣,使用现代C++,您可以使用GCC和Clang/LLVM实现这种类型的优化,您甚至可以使用以下switch
语句:
#includestatic constexpr unsigned long hash(const char *str) { unsigned long hash = 5381; int c = *str; while ((c = *str++)) hash = ((hash << 5) + hash) + c; return hash; } int main( int argc, char **argv ) { size_t size=0; printf("%ld",hash("exit")); // I want this to evaluate in compile time switch((unsigned long)5 /* i.e. some number */) { case hash("exit"): // etc ; } return 0; }
这是C++ 14代码,您需要使用-std=c++14
它来编译它(或使用GCC 6+,这是默认值).(当然,代码不是惯用的C++ - 它旨在尽可能接近前面的例子).