1

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?

Aleksi Torhamo
  • 6,452
  • 2
  • 34
  • 44
  • I've been thinking about this question since shortly after you posted it, but no general solution has come to mind. However, it seems to me that it would be rare to have expressions that can't be pasted to form a valid preprocessing token, so the combination approach would likely work 99% of the time, meaning you would almost never need to use parentheses. Actually, can you come up with an example where you would need them in code that people would be at all likely to write? – Charles Ofria Oct 11 '15 at 20:05
  • @CharlesOfria: I think having a pointer to a value so that you need to dereference it, or having a variable that you need to negate might be the most likely scenarios. (ie. `*var` or `-var`) I'm also not sure if it's better to have it work 99% of the time without parenthesis, or make it always require parenthesis - after all, at least the latter is consistent; Needing parenthesis is definitely exceptional, but it's probably easier to remember if you always need to remember it, rather than having a 1-in-a-100 case where you need to remember it or you get a bunch of weird errors. – Aleksi Torhamo Oct 11 '15 at 21:22
  • I do not understand. `is a (small) number` what is the definition of a (small) number? Does https://stackoverflow.com/questions/49480442/detecting-integer-constant-expressions-in-macros and such answer your question? I.e. is a "(small) number" lower than 100, or is it a number that is a constant? – KamilCuk Aug 30 '21 at 17:50

0 Answers0