Reputation: 8610
I need a Macro that would be evaluated at compile time, something like:
#define FIND_RANGE(x) \
if x>16 \
32 \
elif x>8 \
16 \
elif x>4 \
8 \
elif x>2 \
4 \
elif x>1 \
2 \
else \
1 \
endif \
So the code
#define S1 FIND_RANGE(7)
unsinged int i = S1;
would be sent to compiler as
unsinged int i = 8;
Can this simple algorithm be done so it is evaluated at compile time?
Upvotes: 4
Views: 5369
Reputation: 8610
Turns out it is doable, and even simple:
#define POW00 1.0f
#define POW01 2.0f
#define POW02 4.0f
#define POW03 8.0f
#define POW04 16.0f
#define POW05 32.0f
#define POW06 64.0f
#define POW07 128.0f
#define POW08 256.0f // use some nicer pow2 constant generation
#define SCALE(x) ( \
x > POW07 ? POW08 : \
x > POW06 ? POW07 : \
x > POW05 ? POW06 : \
x > POW04 ? POW05 : \
x > POW03 ? POW04 : \
x > POW02 ? POW03 : \
x > POW01 ? POW02 : \
x > POW00 ? POW01 : POW00 \
) // end SCALE
Example:
int main()
{
float a = (float)SCALE(7.0f);
}
This gets evaluated at compile time to
float a = 8.0f;
Upvotes: 1
Reputation: 37954
While C has no constexpr
functions, both GCC and Clang can evaluate simple functions at compile-time with -O1
. The related optimization is known as constant folding.
The following C code:
#include <stdio.h>
static inline unsigned int findRange(unsigned int x)
{
if (x > 16)
return 32;
else if (x > 8)
return 16;
else if (x > 4)
return 8;
else if (x > 2)
return 4;
else if (x > 1)
return 2;
return 1;
}
int main(void)
{
unsigned int i = findRange(7);
printf("%u\n", i);
return 0;
}
results into x86-64 assembly code (reference: godbolt.org/g/kVYe0u):
main:
sub rsp, 8
mov esi, 8
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0
add rsp, 8
ret
As you can see, the call to findRange
is subsituted by value, which is computed at compile-time.
This works even when findRange
is defined as normal (non-inline) function with external linkage.
Upvotes: 9
Reputation: 9476
For a bit of recreation, I translated that bit twiddling hack mentioned by Sander into a macro:
#define XS(x,y) (x | (x>>y))
#define FR(x) XS(XS(XS(XS(XS(x-1,1),2),4),8),16)+1
So FR(7) should give 8 at compile time, and so on.
(*But for all practical purposes an answer by Grzegorz Szpetkowski is the one to refer to.)
Upvotes: 2
Reputation: 14400
I don't think you can do that that easy. The problem is that the conditionals available to the preprocessor comes as preprocessor directives.
What you can do however is to use the #include
directive creatively to create more advanced constructs. Create find-range.mac
as:
#if x>16
32
#elif x>8
16
#elif x>4
8
#elif x>2
4
#elif x>1
2
#else
1
#endif
#undef x
and then use it as:
int i =
#define x 7
#include "find-range.mac"
;
Which should expand to something like:
int i =
8
;
Another trick that does not go all the way is to do replace FIND_RANGE(x)
with FIND_RANGEx
by gluing and then define FIND_RANGEx
appropriately. This requires x
to be in a finite set of values:
#define FIND_RANGE(x) FIND_RANGE ## x
#define FIND_RANGE1 1
#define FIND_RANGE2 2
#define FIND_RANGE3 4
#define FIND_RANGE4 4
#define FIND_RANGE5 8
#define FIND_RANGE6 8
#define FIND_RANGE7 8
#define FIND_RANGE8 8
// etc...
Upvotes: 3