gpt4 book ai didi

c - GCC宏扩展调用另一个宏

转载 作者:太空狗 更新时间:2023-10-29 17:12:28 25 4
gpt4 key购买 nike

我正在用 C (gcc) 编写一个应用程序,它执行大量字符串比较。始终是一个未知/动态字符串,其中包含一长串编译时常量字符串。所以我想我对动态字符串进行哈希处理,并将生成的哈希值与常量字符串的预计算哈希值进行比较。

为了做到这一点,我在一个函数中使用了哈希算法(用于动态运行时字符串)并将其作为一个宏,以便 gcc 在编译时计算哈希值。

我明白了:

#define HASH_CALC(h, s) ((h) * 33 + *(s))
#define HASH_CALC1(s) (HASH_CALC(hash_calc_start, s))
#define HASH_CALC2(s) (HASH_CALC(HASH_CALC1(s), s + 1))
#define HASH_CALC3(s) (HASH_CALC(HASH_CALC2(s), s + 2))
#define HASH_CALC4(s) (HASH_CALC(HASH_CALC3(s), s + 3))
#define HASH_CALC5(s) (HASH_CALC(HASH_CALC4(s), s + 4))
//--> cut ... goes till HASH_CALC32

static const unsigned long hash_calc_start = 5381;
unsigned long hash_str (const char* c);

void func () {
//This string is not constant ... just in this case to show something
char dynStr = "foo";
unsigned long dynHash = hash_str (dynStr);

//gcc produces a cmp with a constant number as foo is hashed during compile-time
if (dynHash == HASH_CALC3("foo")) {
}
}

现在回答问题:

是否可以创建一个扩展为 HASH_CALCX(s) 的宏,其中 X 是传递给宏的常量字符串的长度?

//Expands to HASH_CALC3("foo")
if (dynHash == HASH_CALCX("foo")) {
}
//Expands to HASH_CALC6("foobar")
if (dynHash == HASH_CALCX("foobar")) {
}

我试过了,但没用。

#define HASH_STRLEN(x) (sizeof(x)/sizeof(x[0])-1)
#define HASH_MERGE(x,y) x ## y
#define HASH_MERGE2(x,y) HASH_MERGE(x,y)
#define HASH_CALCX(s) (HASH_MERGE2(HASH_CALC, HASH_STRLEN(s))(s))

谢谢!

最佳答案

你可以使用三元运算符:

#define HASH_CALCX(s)                \
(strlen(s) == 5 ? HASH_CALC5(s) : \
strlen(s) == 4 ? HASH_CALC4(s) : \
strlen(s) == 3 ? HASH_CALC3(s) : \
strlen(s) == 2 ? HASH_CALC2(s) : \
strlen(s) == 1 ? HASH_CALC1(s) : some_error)

要使其可行,将取决于编译器的优化,将此表达式减少为单个数值常量。

更新:使用 gcc 的工作示例。如果优化级别设置为 1 很好,但不是 0。

设 foox.c 为:

#include <string.h>
#include <stdio.h>

#define HASH_CALC(h, s) ((h) * 33 + *(s))
#define HASH_CALC1(s) (HASH_CALC(5381, s)) // hash_calc_start = 5381
#define HASH_CALC2(s) (HASH_CALC(HASH_CALC1(s), s + 1))
#define HASH_CALC3(s) (HASH_CALC(HASH_CALC2(s), s + 2))
#define HASH_CALC4(s) (HASH_CALC(HASH_CALC3(s), s + 3))
#define HASH_CALC5(s) (HASH_CALC(HASH_CALC4(s), s + 4))

#define HASH_CALCX(s) \
(strlen(s) == 5 ? HASH_CALC5(s) : \
strlen(s) == 4 ? HASH_CALC4(s) : \
strlen(s) == 3 ? HASH_CALC3(s) : \
strlen(s) == 2 ? HASH_CALC2(s) : \
strlen(s) == 1 ? HASH_CALC1(s) : 0)

int main(void) {
printf("%d\n", HASH_CALCX("foo"));
return 0;
}

然后 gcc -S -O1 foox.c 给出:

        .file       "foox.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "%d\n"
.text
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $20, %esp
movl $193491849, 4(%esp)
movl $.LC0, (%esp)
call printf
movl $0, %eax
addl $20, %esp
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-54)"
.section .note.GNU-stack,"",@progbits

更新 2:作为一个次要增强,我肯定会尝试添加一个编译时“断言”来验证只有文字一定长度的字符串传递给宏,因为我很容易出错。例如,将上面的修改为:

#define ASSERT_zero(e) (!sizeof(struct{int:!!(e);}))
#define HASH_CALCX(s) (ASSERT_zero(strlen(s) <= 5) + \
(strlen(s) == 5 ? HASH_CALC5(s) : \
etc

ASSERT_zero() 宏类似于 BUILD_BUG_ON_ZERO(),并使用“sizeof bitfield”技巧。它产生:

  • 编译错误,当e为假时,或者
  • 零值。

ASSERT_zero() 不适用于 C++。而且这个额外的检查对 VS IIRC 不起作用,因为 VS 不将 strlen("foo") 视为编译时常量。

关于c - GCC宏扩展调用另一个宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21164549/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com