13

I have a macro that looks like this:

M(id,...)

I would like it to expand to nothing if id == 0 and something else otherwise.

Is this possible? If so, how?

My first instinct was to try something like this:

#define M(id,...) M##id(__VA_ARGS__)
#define M0(...)
#define M_NOT_0(...) some_code(__VA_ARGS__)

But the last line here is obviously not valid, and I can't figure out a way to make this pattern work.

Notes:

  • The id is an integer between 0 and 255, but ideally I'd like to avoid creating 256 separate macro definitions.
  • Please do not challenge the premise of the question. The M(id,...) macro itself cannot be changed.
  • No assumptions about the final code being expanded to can be made.
2501
  • 25,460
  • 4
  • 47
  • 87
Ryan Hilbert
  • 1,805
  • 1
  • 18
  • 31
  • 1
    May I ask what is `id`? – sidyll Dec 18 '16 at 00:46
  • 3
    And, also, what is `some_code`? – Peter Dec 18 '16 at 00:56
  • @Ryan did you try #if, #elif, #else, and #endif Directives ? – NeoR Dec 18 '16 at 01:35
  • I do not know how to further clarify what `id` is, but I added "No assumptions about the final code being expanded to can be made" to the question because the actual contents of `some_code` are irrelevant. This question is looking for a solution to the general problem described, if it exists. If making 256 separate macro definitions for each possible value of `id` is really the only solution, it will be the accepted answer. This would not be ideal however, since this solution cannot be generalized to cases where there are effectively infinite non-zero possibilities for `id`. – Ryan Hilbert Dec 18 '16 at 02:24
  • 2
    Why are you not calling appropriate (possibly `inline`) functions? Will each macro be invoked more than once, or will they be used just once? What makes `id` change at the point of invocation? Will it always be a simple numeric constant, or will it ever need to be an expression? I think you're headed in the wrong direction — you should avoid macros when possible. And yes, that means I'm challenging the premise of the question. Ill-advised premises should be challenged. – Jonathan Leffler Dec 18 '16 at 04:16
  • 2
    @JonathanLeffler No, they should not, because it is not constructive to do so here, **hence the note asking you not to**. I asked the question because I'm working with a code base where this would be useful. The context explaining why is too large to fit into a comment and irrelevant to the question. If you think what I'm asking is impossible, then by all means up-vote the answer saying so, but responding to a question about macros with "you should avoid macros when possible" is not helpful. To your other questions, the `M` macro is used repeatedly and `id` is always a simple numeric constant. – Ryan Hilbert Dec 18 '16 at 05:32
  • 1
    The correct solution for _the actual problem_ is most likely to get rid of all these macros in the first place. Whenever you come up with the need for variadic macros, or other forms of complex macro structures, or macro meta programming, then that's almost always a certain indication of poor program design. – Lundin Dec 23 '16 at 08:27

3 Answers3

7

The macro CHECK0 works based on the number of tokens encountered in a macro.

If a token passed to the macro has a value of 0, it expands to HIDDEN0 that expands to two tokens. Otherwise it expands to a macro that doesn't exist, and this macro is considered as one token.

The macro SECOND then selects the second token. If HIDDEN0 is encountered, it selects 0, if the non-existing token is encountered, it selects 1.

That result is then joined with a prefix selected by the user. This is seen in macros HELLO, and PRINT. One is a plain macro, the other is a macro function. The resulting macro is either HELLO0 or HELLO1. One is defined as something useful, other is defined as empty. The same goes for PRINT.

#include <stdlib.h>
#include <stdio.h>

#define EXPAND(...) __VA_ARGS__ //needed for MSVC compatibility

#define JOIN_EXPAND( a , b )     a##b
#define JOIN( a , b )            JOIN_EXPAND( a , b )

#define SECOND_EXPAND( a , b , ... )    b
#define SECOND(...)                     EXPAND( SECOND_EXPAND( __VA_ARGS__ ) )

#define HIDDEN0             unused,0
#define CHECK0( value )     SECOND( JOIN( HIDDEN , value ) , 1 , unused )

#define HELLO0           puts( "HELLO" )
#define HELLO1    
#define HELLO( value )   JOIN( HELLO , CHECK0( value ) )

#define PRINT0( ... )           printf( __VA_ARGS__ )
#define PRINT1( ... )
#define PRINT( value , ... )    JOIN( PRINT , CHECK0( value ) )( __VA_ARGS__ )

int main( void )
{
    HELLO( 54545 ) ;        //evaluates to nothing
    HELLO( 1 ) ;            //evaluates to nothing
    HELLO( 0 ) ;            //evaluates to puts( "HELLO" )

    PRINT( 861151 , "Some values: %lf %d\n" , 3.13 , 12345 ) ;  //evaluates to nothing
    PRINT( 1 , "Some values: %lf %d\n" , 3.13 , 12345 ) ;       //evaluates to nothing
    PRINT( 0 , "Some values: %lf %d\n" , 3.13 , 12345 ) ;       //evaluates to printf( "Som

    printf( "%d %d %d\n", CHECK0( 0 ) , CHECK0( 1 ) , CHECK0( 123456 ) ) ; //outputs 0 1 1
    return EXIT_SUCCESS ;
}
Ryan Hilbert
  • 1,805
  • 1
  • 18
  • 31
2501
  • 25,460
  • 4
  • 47
  • 87
  • 1
    This is exactly the sort of preprocessor sorcery I was looking for. I ended up using the first 7 macros to create a general-purpose `IF(condition)(code)` macro that generates `code` only if `condition` is nonzero. It looks like this: `#define IF(...) JOIN(IF,CHECK0(__VA_ARGS__))` followed by `#define IF0(...)` and `#define IF1(...) __VA_ARGS__` – Ryan Hilbert Dec 26 '16 at 08:46
  • @RyanHilbert Yay, my first bounty! Thanks. – 2501 Dec 28 '16 at 06:41
5

Based almost entirely on @PaulFultzII's superbly detailed answer to this question, here is a way to do this:

#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__
#define IIF(c) PRIMITIVE_CAT(IIF_, c)
#define IIF_0(t, ...) __VA_ARGS__
#define IIF_1(t, ...) t
#define CHECK_N(x, n, ...) n
#define CHECK(...) CHECK_N(__VA_ARGS__, 0,)
#define PROBE(x) x, 1
#define COMPL(b) PRIMITIVE_CAT(COMPL_, b)
#define COMPL_0 1
#define COMPL_1 0
#define NOT(x) CHECK(PRIMITIVE_CAT(NOT_, x))
#define NOT_0 PROBE(~)
#define BOOL(x) COMPL(NOT(x))
#define IF(c) IIF(BOOL(c))
#define EAT(...) 
#define EXPAND(id, ...) printf(__VA_ARGS__, id)
#define M(id, ...) IF(id)(EXPAND(id, __VA_ARGS__), EAT(id, __VA_ARGS__))

M(0, "don't ever print this!\n")
M(1, "ok to print %s, %d\n", "with a string")
M(172, "ok to print %d\n")

If we run this through the preprocessor (cpp in the case of the GNU C compiler), we get this:

printf("ok to print %s, %d\n", "with a string", 1)
printf("ok to print %d\n", 172)
Community
  • 1
  • 1
Edward
  • 6,964
  • 2
  • 29
  • 55
0

Assuming some_code() is actually code

This can be tricky: if id is expanded from another token, it could be something like (0) instead of 0, or 0x10 instead of 16. Also, run-time values.

If you have a modern enough compiler, however, you can make use of dead code elimination:

#define M(id, ...) do {             \
    if (id) {                       \
        some_code(__VA_ARGS__);     \
    }                               \
} while (/* CONSTCOND */ 0)

This will do the trick, even in a setting such as…

void
foo(int bar, int baz) {
    M(bar, baz);
}

… and, since id is supposed to be a constant in all other cases, the if will be optimised away (even GCC 3.4.6 without any optimisation flags does that, for both zero and nōn-zero values, I just checked; LLVM will also do that, and many commercial Unix vendor compilers are likely to do so; PCC probably won’t, but is unlikely to be encountered by you).

So, this is not a pure-cpp solution, but one that’s likely to work in the wild, and it doesn’t invoke Undefined Behaviour or similar… fun… either.

The /* CONSTCOND */ is for lint, and the whole do { … } while (0) block is commonly used around macros that contain control constructs, so they can be used universally.

If some_code is not code but an arithmetic expression

In this case, the same solution becomes even shorter:

#define M(id, ...) ((id) ? some_code(__VA_ARGS__) : void)

Instead of void at the end, substitute anything you wish to return if id was zero. (I don’t believe the question warrants this, as writing some_code(__VA_ARGS__) pretty much looks like a function taking arguments, but some commenting person insists on this.)

mirabilos
  • 5,123
  • 2
  • 46
  • 72
  • 1
    "Expands to a code block that conditionally does nothing" is not at all the same thing as "expands to nothing". There are plenty of contexts where that makes a huge difference. – John Bollinger Dec 21 '16 at 17:21
  • True, but here, this is a constant condition, and as such, most compilers optimise the block fully away. I _was_ concentrating on the issue at hand, not speaking very generally. – mirabilos Dec 21 '16 at 18:58
  • "Compilers will optimize it away" is not sufficient, because it's not necessarily applicable. You're assuming that the macro is expanded only in contexts where a `do/while` loop can appear. If it is expanded anywhere else (e.g. in a variable declaration) then the compiler not only won't optimize it away, it will reject the code altogether. – John Bollinger Dec 21 '16 at 19:21
  • “some_code” is pretty explicitly code, not arithmetic expressions. But that case can also be accounted for, I'll update my post accordingly. – mirabilos Dec 21 '16 at 20:00
  • depends, it's actually not a syntax error if it's never used (`x = M(y, z), 1;`). But it's a nice placeholder, and it teaches people to think before blindly copy-pasting from here ;-) akin to [Grml](http://grml.org/tips/) using `/dev/ice` instead of `/dev/sda` in documentation to prevent people from shooting themselves into the foot from blindly copy/pasting from documentation. And I even explained it. – mirabilos Dec 21 '16 at 20:18
  • @Jonathan: perhaps they did but that would be a type error. You need a value with the same type as `some_code(...)` and good luck figuring that out. You could cast both arguments to `(void)` but then you no longer have an expression. – rici Dec 21 '16 at 20:25