Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Macro with either 0 or 1 parameter [duplicate]

I want to create a C++ macro that can either take in no parameters, or a single parameter.

FUNC() or FUNC(1) should both work.

I was wondering how to use variadic macros in order to achieve this.

Func() should map to do_something_different.

Func(1) should map to do_something(1).

Note that they are different statements

like image 440
user2816456 Avatar asked Jan 09 '23 18:01

user2816456


1 Answers

Variadic macros can be helpful in similar cases, but you don't need them (and in fact can't use them) for this specific example, because it doesn't involve a variable number of arguments but only the changing nature of one argument (note that a nonexistent token can be a valid macro argument for many purposes, including this one).

You can dispatch on an argument if you know all of its forms in advance (or if you don't care about some not resulting in further expansions), using the ## operator:

#define FUNC(X) FUNC_IMPL_ ## X

#define FUNC_IMPL_ do_something_different
#define FUNC_IMPL_1 do_something(1)

FUNC()   // -> do_something_different
FUNC(1)  // -> do_something(1)

## concatenates two tokens to form one, which is then subject to re-expansion, so you can use it to modify macro names based on arguments and thus expand to different outputs. An empty argument is allowed to be used in this way too, handily.

There are many ways to make this much more complicated, with varying argument numbers and stages of expansion and so on, depending on what else you want to achieve.

e.g. detecting any argument to FUNC and passing it through to do_something requires quite a bit of expansion trickery:

#define ONE_OR_TWO(...) ONE_OR_TWO_(__VA_ARGS__, 2, 1,)
#define ONE_OR_TWO_(_1, _2, X, ...) X
#define CAT(A, B) CAT_(A, B)
#define CAT_(A, B) A ## B

#define FUNC(X) CAT(DO_SOMETHING_, ONE_OR_TWO(FUNC_IMPL_ ## X))(X)
#define FUNC_IMPL_ ,

#define DO_SOMETHING_2(_) do_something_different
#define DO_SOMETHING_1(X) do_something(X)

FUNC()   // -> do_something_different
FUNC(1)  // -> do_something(1)
FUNC(2)  // -> do_something(2)

This also demonstrates a quirk of ##, which is that it pastes before expanding its operands, so it needs a helper macro if you want to expand-and-paste. On the other hand, the fact that empty parentheses are detected as containing an invisible argument means we have to rely on ## to detect them.

Using macros for anything nontrivial can be quite complicated. (I'm not condoning the use of this style.)

like image 165
Leushenko Avatar answered Jan 15 '23 17:01

Leushenko