4

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

a3f
  • 8,517
  • 1
  • 41
  • 46
user2816456
  • 569
  • 2
  • 5
  • 15
  • Does it have to be a macro? Do you have access to C++11? – James Adkison Mar 07 '15 at 23:07
  • Don't use macros, use overloaded functions instead (Scott Meyers, Effective C++). But this might be useful if you're still interested: https://msdn.microsoft.com/en-us/library/ms177415.aspx – jensa Mar 07 '15 at 23:13
  • 1
    Mapping `func()` to `dosomething` is quite significantly different from mapping `func()` to `dosomething()` and would potentially change the nature of the answers (and the importance of macros to them) if this is really what you intend. – Alex Celeste Mar 07 '15 at 23:19

3 Answers3

5

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.)

Community
  • 1
  • 1
Alex Celeste
  • 12,824
  • 10
  • 46
  • 89
  • Doesn't work for `FUNC(0)`, `FUNC(2)`, or any other values besides `1` – jwfearn Mar 07 '15 at 23:46
  • @jwfearn Well, that's not what the question asked for, but gimme a minute... – Alex Celeste Mar 07 '15 at 23:50
  • sure enough, you're right! – jwfearn Mar 07 '15 at 23:55
  • Actually I think OP meant number of macro arguments will be to zero or one, not that the value of the one-argument form is always `1`. But if it will always be `1`, then your solution is great. – jwfearn Mar 08 '15 at 00:03
  • This should be the accepted answer! Kudos @Leushenko for the pure pre-processor solution! – jwfearn Mar 08 '15 at 00:14
  • 2
    Note that if overloaded macros are actually required, `BOOST_PP_OVERLOAD` cuts out the boilerplate. – chris Mar 08 '15 at 00:33
  • 1
    [This blog post](https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/) (not mine) shows an easily expansible way of accomplishing the same goal (checking if a single argument is an actual argument vs. the empty token). – monkey0506 May 12 '18 at 06:19
3

This is trivial with C++11 variadic macros

#define FUNC(...) do_something(__VA_ARGS__)

This is not possible before C++11, except as an extension.

However, using macros is not necessary in C++ for this at all (and would be considered poor style, since it is unsafe). Simply do them as functions with default arguments.

void FUNC(int x = 0)
{
    do_something(x);
}

FUNC();    // calls FUNC(0)
FUNC(1);

or as an overloaded function

void FUNC()
{
    do_something();  // can remove brackets here, although getting it to compile cleanly would be interesting
}

void FUNC(int x)
{
     do_something(x);
}

FUNC();
FUNC(1);

The latter does assume that do_something() is either overloaded or has an argument with a default value.

These options work in all versions of C++.

Rob
  • 1,966
  • 9
  • 13
  • It's not trivial at all with variadic macros. What you have, `do_something(__VA_ARGS__)`, cannot easily be extended to `do_something_else` (like the OP asks) when `__VA_ARGS__` is empty. But agreed with the rest of your answer: if avoiding macros for this is possible, it's almost certainly better. (I see the OP edited the question, and it wasn't clear from the question initially.) –  Mar 07 '15 at 23:56
  • @Rob, Variadic macros have been part of the preprocessor for decades (since C99), they are not new in C++11. You may be thinking of variadic templates. – jwfearn Mar 08 '15 at 00:07
  • @jwfearn Variadic macros were only part of the preprocessor because most C++ compilers also handle C. They weren't added to C++ until C++11 though; before that, it was the compiler letting you use non-C++ C features. – Alex Celeste Mar 08 '15 at 00:09
  • @hvd: The OP edited his post since I replied. The original version did not have a reference to "do_something_different" like the current version does. – Rob Mar 08 '15 at 04:35
  • @jwfearn: Varadic macros were not part of C++ before C++11. The C99 preprocessor has never been the C++ preprocessor - at least as far as the standards are concerned. As I said, some C++ compilers before 2011 (most of which happen to also be C99 compilers) - but not all - supported it as an extension. – Rob Mar 08 '15 at 04:38
  • @Rob, (at)Leushenko Thanks for the clarification. I've been using ... in my C++ macros for so long I didn't realize it wasn't officially standard. Glad it is now. – jwfearn Mar 10 '15 at 15:02
1
void func(extra) { do_something_different(); }
void func(a, extra) { do_something(a); }
#define FUNC(...) func(__VA_ARGS__, #__VA_ARGS__)

Simpler:

void func() { do_something_different(); }
void func(a) { do_something(a); }
#define FUNC(...) func(__VA_ARGS__)

You don't need variadic macros:

void func() { do_something_different(); }
void func(a) { do_something(a); }
#define FUNC func

You don't even need macros at all:

void FUNC() { do_something_different(); }
void FUNC(a) { do_something(a); }
jwfearn
  • 28,781
  • 28
  • 95
  • 122
  • And if you just call the functions FUNC, you don't need a macro at all. – user253751 Mar 07 '15 at 23:29
  • @immibis, absolutely! OP wanted a macro though. Update answer with your suggestion. Thanks! – jwfearn Mar 07 '15 at 23:34
  • "you don't need a macro at all" depends on the actual use case. OP's use case may not have required it, but there are still use cases where variadic macros are the best or even only solution, and knowing how to distinguish between an empty token and an actual argument becomes crucial. – monkey0506 May 12 '18 at 06:22