15

Is there a way to write a C preprocessor macro that expands to different things depending on what argument it receives?

#define foo() ???

/* 1 */
foo(name)

/* 2 */
foo(_)

Desired result:

/* 1 */
int name;

/* 2 */
/*ignore*/

Yes, I know macros are evil. I'm asking this mostly out of curiosity.

Cœur
  • 37,241
  • 25
  • 195
  • 267
hugomg
  • 68,213
  • 24
  • 160
  • 246
  • 14
  • 2
    Could you explain the purpose, or perhaps give a better example? The parameter would have to be fixed at compile time, which means you could just as well use two different macro names. – lurker Sep 25 '13 at 17:07
  • @mbratch: In this particular case, I'm using a macro to define a set of variables but some variables are optional and I want to avoid defining them. Multiple macro names wouldnt work well because of a combinatorial explosion: with two variable names I would need separate macros for `foo(a,b)`, `foo(a,_)`, `foo(_,b)` and `foo(_,_)`. – hugomg Sep 25 '13 at 17:10
  • 1
    You're sort of trying to implement a form of overloading then. Perhaps you should look into variadic macros. See this question http://stackoverflow.com/questions/3420459/c-preprocessor-macro-overloading?rq=1 – Theodoros Chatzigiannakis Sep 25 '13 at 17:18
  • @TheodorosChatzigiannakis: The problem is that I need to overload over the values I pass the macro, not over the number of arguments. – hugomg Sep 25 '13 at 17:20

3 Answers3

17

To expand on Gavin Smith's answer, you actually can check conditions within a macro expansion:

#define FOO_name 1
#define FOO__ 0

#define CONC(a,b) a##_##b

#define IF(c, t, e) CONC(IF, c)(t, e)
#define IF_0(t, e) e
#define IF_1(t, e) t

#define FOO(x) IF(CONC(FOO,x), int x;, )

FOO(name) // -> int name;
FOO(_)    // -> /*nothing*/

If you're feeling adventurous you can easily extend IF to allow commas, suppress macro expansion, etc. with helper macros.

As above though this does require that you know all of the desired names in advance.

Alex Celeste
  • 12,824
  • 10
  • 46
  • 89
9

Perhaps try some multi-stage macro expansion? This is the strategy used by the Boost preprocessor/control/if library.

#define FOO_NAME 1
#define FOO__ 2

#define CONC(a,b) a##_##b
#define FOO(x) CONC(FOO,x)

I don't think there is any way to check conditions within a C macro expansion.

The best thing I could come up with is to covert the macro arguments to a string literal using the # stringizing operator, and then checking using run-time functions. (This won't work for your case, though, where you want to output variable declarations.)

For example, the following prints "011":

#define FOO(x) (strcmp("NAME", #x) ? 1 : 0)

main()
{
    printf("%d", FOO(NAME));
    printf("%d", FOO(1));
    printf("%d", FOO(2));
}

The compiler would likely optimize the strcmp comparisons at compile-time so it would be no more inefficient than it would have been had genuine pre-processor conditionals been available. However, making FOO a normal function would be clearer and probably just as efficient.

hugomg
  • 68,213
  • 24
  • 160
  • 246
Gavin Smith
  • 3,076
  • 1
  • 19
  • 25
  • Interesting trick! But is there a way where I don't need to enumerate all the possibilities for the "1" case? For example, if its `_` then the macro yields 2 if its anything else it yields 1? – hugomg Sep 25 '13 at 17:22
  • I updated my answer with a limited workaround that won't work for your original question. – Gavin Smith Sep 25 '13 at 17:57
  • Unfortunately, I can't use a runtime test in my case. I'm starting to think that the first solution you game is the best I'm gonna get. – hugomg Sep 25 '13 at 18:02
  • @hugomg what you want is possible, but it requires a lot of dirty preprocessor tricks: https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms I will add an answer – yyny Sep 20 '18 at 20:40
2

With the tricks described here, it is possible to do what you want at compile time.

You could use the EQUAL macro defined at the end of the document, and do this:

#define COMPARE__(x) x
#define OPTION_0(x) int x;
#define OPTION_1(x) /* nothing */
#define foo(x) CAT(OPTION_, EQUAL(_, x))(x)

foo(name1) // int name1;
foo(_)     // /* nothing */
foo(name2) // int name2;
yyny
  • 1,623
  • 18
  • 20