GCC宏扩展调用另一个宏

Xat*_*ian 7 c macros gcc

我正在用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")) {
    }
}
Run Code Online (Sandbox Code Playgroud)

现在问题:

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

//Expands to HASH_CALC3("foo")
if (dynHash == HASH_CALCX("foo")) {
}
//Expands to HASH_CALC6("foobar")
if (dynHash == HASH_CALCX("foobar")) {
}
Run Code Online (Sandbox Code Playgroud)

我试过这个,但它不起作用.

#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))
Run Code Online (Sandbox Code Playgroud)

谢谢!

Mar*_*ian 1

不幸的是,在 C 中,字符串分解不是常量表达式。以下内容无效:

switch (x) {
case "a"[0]:
  ...
}
Run Code Online (Sandbox Code Playgroud)

预处理器中的常量表达式类似。以下内容无效:

#if "a"[0] == 'a'
  ...
#endif
Run Code Online (Sandbox Code Playgroud)

如果您不需要在switch语句中使用哈希码,为什么不在程序的初始化阶段预先计算这些值,将结果存储到变量中,然后在测试中简单地使用它们?

如果您在switch语句中需要它们,我建议将字符串拆分为字符。在你的情况下,这将给出如下宏:

#define HASH_CALC(...) HASH_CALC0(5381, __VA_ARGS__, 0, 0, 0, 0, 0)
#define HASH_CALC0(p, x, ...) (x == 0 ? (p) : HASH_CALC1((p)*33+x, __VA_ARGS__)))
#define HASH_CALC1(p, x, ...) (x == 0 ? (p) : HASH_CALC2((p)*33+x, __VA_ARGS__)))
#define HASH_CALC2(p, x, ...) (x == 0 ? (p) : HASH_CALC3((p)*33+x, __VA_ARGS__)))
#define HASH_CALC3(p, x, ...) (x == 0 ? (p) : HASH_CALC4((p)*33+x, __VA_ARGS__)))

...
#define HASH_CALC64(p, x, ...) (x == 0 ? (p) : -1 /* shall never be used */)
Run Code Online (Sandbox Code Playgroud)

HASH_CALC需要可变数量的字符作为参数。例如,以下是有效代码:

switch (x) {
case HASH_CALC('f', 'o', 'o'):
   ...
case HASH_CALC('f', 'o', 'o', 'b', 'a', 'r'):
   ...
}
Run Code Online (Sandbox Code Playgroud)