1

Is there a way to check at compile time if the argument of a macro is an integer literal, and evaluate the macro differently in that case?

#include <stdio.h>

#define VALUE_0 0
#define VALUE_1 2
#define VALUE_2 4
#define VALUE_3 6
#define VALUE_4 8

#define VALUE(_idx_) VALUE_ ## _idx_

#define VALUE_(_idx_) 2*(_idx_)

int main() {
    printf("%i\n", VALUE(3));
    printf("%i\n", VALUE_(1+2));
}

VALUE(3) is always resolved at compile-time, but only works if 3 is an integer literal. VALUE_(3) works for any argument type, but may be result in an expression that is computed at runtime (in a more complex case), and make compiler optimizations impossible.

If there a way to write the macro such that is automatically resolves to VALUE_ or to VALUE, depending if the argument is an integer literal.

Edit:

It is for a C program, or more specifically OpenCL C. It seems that for some OpenCL C compilers (for example NVidia nvcc and Intel), an expression like VALUE(idx) does not always get resolved at compile time, even when the argument is a constant. (Or at least the kernel does not get auto-vectorized if it contains such an expression.) For example if VALUE() resolves to a call of an inline function containing a switch statement, or to a lookup of a constant array, it does not work, but if it is an nested ?: expression, it works. VALUE_ would be guaranteed to resolve to a constant.

Because I'm generating C source code at runtime from the host and passing it to the OpenCL C compiler, it would be useful to not have to generate two different macros for each array.

tmlen
  • 8,533
  • 5
  • 31
  • 84
  • 7
    C or C++? Pick one. It matters. – nwp Mar 12 '18 at 14:12
  • I feel like even if it is possible it could lead to a lot of confusion. How would it handle something that's `const` or `constexpr`? Just because it's not a literal doesn't mean it isn't computed at compile time. Even in your example any sane compiler would compute `1+2` at compile time. – Kevin Mar 12 '18 at 14:14
  • 2
    @nwp: Yes, to emphasise, nobody is being pedantic here. **It matters a lot in this particular case**. C and C++ have diverged on their handling of this. – Bathsheba Mar 12 '18 at 14:14
  • Are you sure that `VALUE_(3)` is not already resolved at compile time? Converting `2*(3)` to `6` is fairly easy optimization for the compiler. – user694733 Mar 12 '18 at 14:17
  • @Bathsheba I. e. they've been using different pre-processors since ??? – Aconcagua Mar 12 '18 at 14:24
  • @Aconcagua: I'm thinking more in terms of `_Generic` for C, and templates for C++. – Bathsheba Mar 12 '18 at 14:24
  • Preprocessor do no know the type of the literals. Literals and types are defined in next processing phase. (from language definition stand point). So it should not be possible (in a portable way). Also I do not remember any compiler which add such support. [on normal function, you have some compiler support (non portable)] – Giacomo Catenazzi Mar 12 '18 at 14:29
  • @Bathsheba But non of these are used anywhere. As they are, these macros could be in a header intended for usage in both C and C++, I don't see what's the problem in the *particular* case - 100% agreeing in the *general* case, of course. – Aconcagua Mar 12 '18 at 14:38
  • @Aconcagua the **solution** would use either `_Generic` or templates. That the question contains a C/C++ polyglot is irrelevant. – Quentin Mar 12 '18 at 15:14
  • @Quentin Now I got curious, as this goes beyond my imagination - how would you solve with any of these the question asked??? Doesn't matter which one you present, the other one I'll certainly get myself... (Ah, and please don't use wand box, I never see any contents if following links to there...) – Aconcagua Mar 12 '18 at 15:24
  • @Aconcagua I don't know, I'm just rewording Bathsheba's comment :) – Quentin Mar 12 '18 at 15:26
  • @Quentin Bah, get it myself, the edit hinting me to it... In C++, a constexpr function (doing the switch or table lookup) could have helped - which we don't have in C... – Aconcagua Mar 12 '18 at 15:34

2 Answers2

1

I'd recommend always using the latter:

#define VALUE_(_idx_) 2*(_idx_)

If the argument is a constant or a constant expression, the resulting expression after the preprocessor will be evaluated by the compiler. If it is not, it will be evaluated at runtime.

The only difference between the two macros in the case of an integer literal is whether the preprocessor gives you the final result or whether the compiler does. In both cases, there is no runtime overhead, so better to go with the one that gives you the most flexibility.

dbush
  • 205,898
  • 23
  • 218
  • 273
0

I'll turn the problem on its head and suggest that you try forcing the compiler to compute the value (with the additional benefit of asserting that the value is, in fact, a compile-time constant):

#define MAKE_ME_CONSTANT(x) sizeof((struct{char c[x];}){{0}}.c)

This (tweaked from an earlier answer of mine) declares an anonymous structure with a char-array member whose size is your constant. It then instantiates it with a compound literal and retrieves the member's size. All of this is part of the unevaluated operand of a sizeof, so I expect any compiler to make it a constant.

Note: I didn't use the simpler sizeof(char[x]) because that could be a VLA. But VLAs can't be structure members, so we're good here.

Hence, you get:

#define VALUE(_idx_) MAKE_ME_CONSTANT(2*(_idx_))
Quentin
  • 62,093
  • 7
  • 131
  • 191