I am writing an application in C (gcc) which does a lot of string-comparison. Always one unknown/dynamic-string with a long list of compile-time constant strings. So I figured I hash the dynamic string and compare the resulting hash with precomputed hashes of the constant strings.
The do this I have the hash-algorithm in a function (for the dynamic runtime-strings) and as a macro so that gcc evaluates the hash during compile-time.
I got this:
#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")) {
}
}
Now to the question:
Is it possible to create a macro which expands to HASH_CALCX(s) where X is the length of the constant string passed to the macro?
//Expands to HASH_CALC3("foo")
if (dynHash == HASH_CALCX("foo")) {
}
//Expands to HASH_CALC6("foobar")
if (dynHash == HASH_CALCX("foobar")) {
}
I tried this but it does not work.
#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))
Thank you!
Here is an example of how to run another macro from a macro using the Call Statement. Just type the word Call then space, then type the name of the macro to be called (run). The example below shows how to call Macro2 from Macro1. It's important to note that the two macros DO NOT run at the same time.
You cannot define macros in other macros, but you can call a macro from your macro, which can get you essentially the same results.
Macro expansion is an integral part of eval and compile . Users can also expand macros at the REPL prompt via the expand REPL command; See Compile Commands. Macros can also be expanded programmatically, via macroexpand , but the details get a bit hairy for two reasons. The second complication involves eval-when .
3.3 Macro Arguments To invoke a macro that takes arguments, you write the name of the macro followed by a list of actual arguments in parentheses, separated by commas. The invocation of the macro need not be restricted to a single logical line—it can cross as many lines in the source file as you wish.
You could used the ternary operator:
#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)
For this to be viable would depend the compiler's optimization reducing this expression to a single numerical constant.
Update: Worked-out example using gcc. Fine if the optimization level is set to 1, but not for 0.
Let foox.c be:
#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;
}
Then gcc -S -O1 foox.c
gives:
.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
Update 2: As a minor enhancement, I would definitely try to add a compile-time 'assert' to verify that only literal strings of a certain length are passed to the macro, because I am error-prone. For example, modify the above to read:
#define ASSERT_zero(e) (!sizeof(struct{int:!!(e);}))
#define HASH_CALCX(s) (ASSERT_zero(strlen(s) <= 5) + \
(strlen(s) == 5 ? HASH_CALC5(s) : \
etc
The ASSERT_zero()
macro is similar to BUILD_BUG_ON_ZERO(), and uses the 'sizeof bitfield' trick. It yields either:
e
is false, orThis ASSERT_zero()
doesn't work for C++. And this extra check won't work for VS IIRC, because VS doesn't regard strlen("foo")
as a compile-time constant.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With