NirMH
NirMH

Reputation: 4929

How to define a C macro that uses another macro?

I have the following scenario...

Header file:

#define TRIG_INDEX 200    
#define PATH(target_p) some.path.to.target##target_p

Source file:

read_from_target(PATH(TRIG_INDEX));

As the PATH macro appends the target_p to the text at the end, compilation fails as some.path.to.targetTRIG_INDEX is not a valid path.

I was expecting to get read_from_target(some.path.to.target200) in the above scenario.

How can I (if at all) define the macros to accept such scenario?

Upvotes: 1

Views: 255

Answers (2)

Lundin
Lundin

Reputation: 213408

The pre-processor macro expansion/replacement rules for function-like macros are rather intricate. Basically it does so in the following order:

  • The stuff after the macro name is known as the replacement list. In this case PATH_TARGET(target_p) - things that PATH should be replaced with.

  • The occurrence of ## or # together with a macro parameter means that the parameter gets replaced with its corresponding pre-processor token sequence (if applicable). In this case, the parameter target_p gets replaced with TRIG_INDEX, so we get some.path.to.target##TRIG_INDEX creating the new pre-processor token some.path.to.targetTRIG_INDEX , which is not as intended.

  • Other macro parameters get expanded. Doesn't apply here.

  • The replacement list is then rescanned for macro names to expand, but it's too late since pre-processor token concatenation with ## has already occurred.

The part "other macro parameters get expanded" above is useful to fix the problem. So macro parameters in the replacement list take precedence over macro names. We can take advantage of this by adding a helper macro:

#define TRIG_INDEX 200
#define EXPAND(tgt) some.path.to.target##tgt
#define PATH(target_p) EXPAND(target_p)

Now in the expansion of the replacement list of PATH, we first get macro parameter expansion: taget_p is replaced and we get EXPAND(TRIG_INDEX). The replacement list is then rescanned for macro names to replace, the pre-processor finds two such macro names: EXPAND and TRIG_INDEX. Both are expanded and so TRIG_INDEX is replaced with 200. Then EXPAND is expanded recursively according to the same rules (## enforcing an expansion of that macro parameter tgt into 200 before anything else).

For details, see the C17 standard chapter 6.10.3 - this chapter is also the one which specifies the behavior of the # and ## operators.

Upvotes: 3

David Ranieri
David Ranieri

Reputation: 41017

The argument must be expanded by the macro:

#define TRIG_INDEX 200
#define PATH_TARGET(x) some.path.to.target##x
#define PATH(target_p) PATH_TARGET(target_p)

Upvotes: 4

Related Questions