You can use 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, compiler optimization will depend on reducing this expression to a single numeric constant.
Update : run example using gcc. Well, 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 small improvement, I would definitely try adding a βclaimβ compilation time to make sure that only character strings of a certain length are passed to the macro, because I'm 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
ASSERT_zero() macro is similar to BUILD_BUG_ON_ZERO (), and uses the "SizeOf bit simulate. This gives either:
- compilation error when
e is false, or - zero value.
This ASSERT_zero() does not work for C ++. And this extra check will not work for VS IIRC because VS does not consider strlen("foo") as a compile-time constant.