I'm trying to figure out if there's a way to return a precalculated value if a macro argument is a (small) number, and return code for calculating the result otherwise, ie. if the argument is a variable/expression.
I'm strictly looking for the best C preprocessor solution - if for nothing else, because I'm curious about whether it can be done - not for "macros are evil, use inline functions", "the compiler will constant fold the expression anyway" or "use another preprocessor".
The best idea I have so far is to require all non-constant-number arguments to the macro to be enclosed in parenthesis, ie. MACRO(2)
vs. MACRO((count))
, since it's possible to detect whether the argument is parenthesized, but that's only marginally better (or worse, depending on viewpoint) than just having
MACRO_CONST
and MACRO
.
An example of what I mean, with the wanted calculation being x+1
:
#define MACRO_1 2
#define MACRO_2 3
#define MACRO_VAR(ARG) ARG + 1
#define MACRO_CONST(ARG) MACRO_ ## ARG
#define MACRO(ARG) IF_IS_PAREN(ARG, MACRO_VAR, MACRO_CONST)(ARG)
#define TEST(...) .., ..
#define THIRD(A, B, C, ...) C
#define CALL(FUNC, ...) FUNC(__VA_ARGS__)
#define IF_IS_PAREN(COND, T, F) CALL(THIRD, TEST COND, T, F, ..)
MACRO(2); // results in 3;
MACRO((var)); // results in (var) + 1;
MACRO(("ab"[var])); // results in ("ab"[var]) + 1;
There's only a small number of supported constant values, so detecting if an individual macro for each constant expands would work, except that it would break expressions that can't be pasted to form a valid preprocessing token.
An example of this:
#define MACRO_1 .., 2
#define MACRO_2 .., 3
#define MACRO(ARG) CALL(SECOND, MACRO_ ## ARG, ARG + 1, ..)
#define SECOND(A, B, ...) B
#define CALL(FUNC, ...) FUNC(__VA_ARGS__)
MACRO(2); // results in 3;
MACRO(var); // results in var + 1;
MACRO("ab"[var]); // error
Combining these two approaches you could make it so that you only have to parenthesize the expression when it doesn't start with [a-z], but it would still be confusing, to say the least, to sometimes have to parenthesize expressions.
Is there any way to make something like this work for arbitrary expressions without requiring the user to hop through any (or jump through less) special hoops? If not, why?