Jonathan Mee
Jonathan Mee

Reputation: 38919

Evaluate a Macro Argument Before Processing

I want to be able to generate these options from a macro:

  1. if(void* temp = func(arg)){ foo(temp, variable);return; }
  2. if(void* temp = func2(arg)){ foo(temp, variable2);return; }
  3. if(void* temp = func3(arg)){ foo(temp, variable3);return; }

And so on, but as you can see 1 is the only special case.

I want to write a macro which takes in a number as a parameter and generates a line this code, potentially with numbers far greater than 3. Unfortunately this requires building in the special case if the user passed a 1 and exercising the general case if they passed any other number. Is there a way to do this?

Upvotes: 2

Views: 830

Answers (1)

H Walters
H Walters

Reputation: 2664

If you really want to use the CPP for this, it's easy enough. An indirect GLUE and an indirect SECOND macro are core tools that you could use:

#define GLUE(A,B) GLUE_I(A,B)
#define GLUE_I(A,B) A##B
#define SECOND(...) SECOND_I(__VA_ARGS__,,)
#define SECOND_I(_,X,...) X

The indirect SECOND allows you to pattern match in the preprocessor. The way that works is that you build a first token, which is normally just a throwaway. But since expansion is indirect, if that first token you build is a macro, it will expand first (namely, as part of argument substitution for the variadic). If that expansion contains a comma, it can shift in a "new" second argument right before the indirection picks the second one. You can use that to build your special cases.

Here's a cpp pattern matcher using this construct that returns its argument unless it is 1, in which case it expands to no tokens:

#define NOT_ONE(N) SECOND(GLUE(TEST_IF_1_IS_,N),N)
#define TEST_IF_1_IS_1 ,

Using that, your macro might be:

#define DISPATCH_CASE(N) \
   if(void* temp = GLUE(func,NOT_ONE(N))){ \
      foo(temp, GLUE(variable,NOT_ONE(N))); \
      return;
   }

Demo (coliru)

Update: Visual Studio version

But I'm on Visual Studio, and I can't make it work. I think the problem is the __VA_ARGS__ expansion works differently on Visual Studio

For VS, I've found another level of indirection of a particular sort (one that separates the macro from its arguments so the arg list can evaluate in a simple (...) context before it's applied) helps it figure out that commas delimit arguments. Typically I would repeat the same pattern in multiple macros to avoid blue paint.

Here, that translates to the slightly uglier:

#define GLUE(A,B) GLUE_C(GLUE_I,(A,B))
#define GLUE_I(A,B) A##B
#define GLUE_C(A,B) A B
#define SECOND(...) SECOND_C(SECOND_I,(__VA_ARGS__,,))
#define SECOND_I(_,X,...) X
#define SECOND_C(A,B) A B

Demo (goldbolt)

Upvotes: 2

Related Questions