7

How can one define a C macro IFARGS(YES, NO, ...) such that invoking IFARGS with no additional arguments produces NO, and invoking IFARGS with one or more arguments produces YES?

I have an answer using GCC (see below), but I'd prefer one for C99 if possible (or a proof of its impossibility).

a3f
  • 8,517
  • 1
  • 41
  • 46
augurar
  • 12,081
  • 6
  • 50
  • 65

2 Answers2

6

In C99 it is possible to detect if a macro argument is empty, but making that robust against all odds that may appear in that argument (arguments that are themselves expanding, contain () and stuff like that) is difficult. My macro package P99 implements such a thing, so you wouldn't have to worry too much. With that your macro can be implemented as

#define IFARGS(YES, NO, ...) P99_IF_EMPTY(__VA_ARGS__)(YES(__VA__ARGS__))(NO())

As its name indicates, P99 is only building on C99 features for that.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • How can one detect an empty argument in C99? – augurar Jan 31 '14 at 07:52
  • 2
    @augurar, this one is in the top search on my favorite search engin :) http://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/ – Jens Gustedt Jan 31 '14 at 08:12
  • Interesting; however, the `ISEMPTY` macro in that post appears to fail when given exactly 16 arguments. – augurar Jan 31 '14 at 08:30
  • After some investigation, I see that `P99_IF_EMPTY` works for 16 arguments, but has some theoretical maximum number of arguments (`P99_MAX_NUMBER`). So this is another good partial solution. – augurar Jan 31 '14 at 08:50
  • Well the current value of `P99_MAX_NUMBER` and the restriction of `P99_IF_EMPTY` (in P99) is at `152` or so, and easily extendable if needed. In any case, the maximum number of macro arguments that a platform *must* accept is 128 or so. It may accept more, but I think writing code with more arguments than that should be somewhat exotic. – Jens Gustedt Jan 31 '14 at 09:07
  • 1
    For real-world applications, this is a great solution. I'm still interested in the theoretical question of whether `##__VA_ARGS__` actually adds any "power" to the macro language, or whether it's just a convenience feature. – augurar Jan 31 '14 at 09:20
  • 1
    @augurar, then why didn't you ask *that* question? – Jens Gustedt Jan 31 '14 at 10:25
2
#define GET(_0, _1) _0  // Return the first of two arguments
#define GET_(_0, _1) _1  // Return the second of two arguments

#define JOIN(_0, _1) _0 ## _1  // Concatenate two arguments
#define EJOIN(_0, _1) JOIN(_0, _1)  // Expand macros and concatenate

#define FIRST(_, ...) _  // Truncate everything after first comma
#define EFIRST(_) FIRST(_)  // Expand argument and pass to FIRST

#define REST(_0, ...) __VA_ARGS__  // Remove everything before first comma

#define GET_GET(...) \
    EJOIN(GET, EFIRST(REST(,,##__VA_ARGS__ _)))  // Branch between GET and GET_

#define IFARGS(YES, NO, ...) GET_GET(__VA_ARGS__)(YES, NO)

Note that if this were possible in C99, then it would be possible to simulate ##__VA_ARGS__, like so:

#define PREPEND_COMMA(...) , __VA_ARGS__
#define NO_COMMA()
#define PREPEND_COMMA_IF_NONEMPTY(...) IFARGS(PREPEND_COMMA, NO_COMMA, __VA_ARGS__)(__VA_ARGS__)

Then any instance of , ##__VA_ARGS__ could be replaced by PREPEND_COMMA_IF_NONEMPTY(__VA_ARGS__).

augurar
  • 12,081
  • 6
  • 50
  • 65
  • This is not really an answer, but merely a complement to your own question. – Jens Gustedt Jan 31 '14 at 07:45
  • @JensGustedt The first code box is a partial solution -- it has the desired behavior, but requires `##__VA_ARGS__`, which is a GCC extension to C99 macro syntax. – augurar Jan 31 '14 at 07:49
  • I noticed that. A little bit of research on SO could have revealed you an answer to that. See the link above. – Jens Gustedt Jan 31 '14 at 07:54
  • @I don't understand. My answer shows that `##__VA_ARGS__` and `IFARGS` are equivalent in power, since with one you can emulate the other. However, this is a nontrivial fact (unless I'm overlooking some easier solution). – augurar Jan 31 '14 at 07:58
  • This might be, but that is not your original question. And I don't think that it has much of a relevance, since that gcc extension is something that can be avoided in most cases with tricks that are simpler than that. – Jens Gustedt Jan 31 '14 at 08:15
  • 1
    @JensGustedt Please, if you can think of an implementation of `IFARGS` that does not require GCC, post it here!! That would be really cool, because as I have proven above, *any* macro that can be implemented with `##__VA_ARGS__` can be implemented with `IFARGS`. – augurar Jan 31 '14 at 08:20
  • 1
    I did that, didn't you see my answer? If you want that reference to `P99_IF_EMPTY` expanded, you'd have to do that by yourself. I don't find your interface `IFARGS` interesting enough for me to do that myself. – Jens Gustedt Jan 31 '14 at 08:25