0

I would like to make a macro F taking a variable number of parameters, that expands to the parameters each separated by a ||. e.g. F(a, b, c) should expand to a || b || c, F(a) should expand to just a, etc.

I know C doesn't support recursive macros. I only need this for at most 4 arguments currently.

I was thinking something like #define F(a, ...) a || F(__VA_ARGS__), and then a second macro to get that to expand 4 times, but I'm not sure at all what that other macro should look like. And I run into the issue of having an empty __VA_ARGS__ at some point. Any other ideas would be much appreciated.

Restrictions: must work with any standard-conforming C99 compiler.

EDIT: I've got this working using Overloading Macro on Number of Arguments, but still curious if there's another solution.

CoffeeTableEspresso
  • 2,614
  • 1
  • 12
  • 30

2 Answers2

4

The basics are pretty easy:

#define COUNT(...) COUNT_(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
#define COUNT_(A0, A1, A2, A3, A4, A5, A6, A7, ...) A7

#define F0()
#define F1(A0) A0
#define F2(A0, A1) A0 || A1
#define F3(A0, A1, A2) F2(A0, A1) || A2
#define F4(A0, A1, A2, A3) F3(A0, A1, A2) || A3

#define F(...) C(F, COUNT(__VA_ARGS__))(__VA_ARGS__)
#define C(X, Y) C_(X, Y)
#define C_(X, Y) X ## Y

where C is the usual two-step concatenation macro. There's one problem left, though: The empty __VA_ARGS__.

Actually, this is not supported by the C standard itself (you'd have to switch to upcoming C++20 for – or maybe C20 backports?). And the task is quite heavy (as is, COUNT() yields to 1, where the first argument in COUNT_ macro is an empty one!). I managed to solve this for a similar task quite a while ago, so you might want to have a look at there, should be easy enough to import the relevant part to here...

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • this is more or less what I ended up doing, so I'll accept this. Thanks! – CoffeeTableEspresso Dec 31 '18 at 05:51
  • @CoffeeTableEspresso Don't think there's a substantially differing way to do it. Noted your edit only after already posting the answer - however the link provided does not cover empty varargs, so suppose the only value of this answer is its link to my other one... – Aconcagua Dec 31 '18 at 06:02
  • GCC has a solution for empty `__VA_ARGS__`: `Historically, GNU CPP has also had another extension to handle the trailing comma: the ‘##’ token paste operator has a special meaning when placed between a comma and a variable argument.` https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html – qrdl Dec 31 '18 at 08:14
  • @qrdl I am well aware of that. Solely: it works for `..., ## __VA_ARGS__`, but not the other way round (`__VA_ARGS__ ## , ...`). – Aconcagua Dec 31 '18 at 08:22
  • @Aconcagua If you are using this counting macro - yes, you need to keep `__VA_ARGS__` in front of other params, but it isn't the only way. Check my old answer: https://stackoverflow.com/questions/2124339/c-preprocessor-va-args-number-of-arguments/2124433#2124433 – qrdl Dec 31 '18 at 09:20
  • @qrdl Hehe, I even upvoted this answer *long* before this comment... Requirements: Macro arguments need to be convertible to int, though, and we need, too, some function 'OR'-ing together the arguments in a loop. Suppose an inline function would allow the compiler loop unrolling... How costly is var-args resolution in functions? Still, this *is* an interesting alternative... – Aconcagua Dec 31 '18 at 10:22
  • @Aconcagua I was talking just about alternative counting macro that supports empty `__VA_ARGS__`, not using the function. Obviously function will add extra runtime processing, something OP doesn't want, and rightly so. – qrdl Dec 31 '18 at 10:38
  • The macro per se is fine, but how would you use it in given case without additional variadic function? If you can't tell, it is like using a hammer to tighten a screw... – Aconcagua Dec 31 '18 at 13:37
  • @Aconcagua I can get around the empty one by doing `F(false, a, b, c)` instead of `F(a, b, c)` (so my smallest case becomes `F(false)`, which expands to just `false`. `F(false, a, b, c)` expands to `false || a || b || c`, which is the same as `a || b || c`. This way I don't really have to deal with empty var-args. – CoffeeTableEspresso Dec 31 '18 at 23:55
  • `false` you only need for empty argument list (`F(false)`). If you are willing to use compiler extensions (`-std=gnu++17` for GCC), then you can have a helper macro: `#define G(...) F(false, ## __VA_ARGS__)` – Aconcagua Jan 03 '19 at 07:29
0

This is kinda heavy duty, but if you can use libraries, then Boost.PP can do much of the heavy lifting for you. It supports both C and C++, and is a header only library (obviously). Your use case will look like this:

#include <boost/preprocessor/variadic/to_seq.hpp>
#include <boost/preprocessor/seq/fold_left.hpp>

#define OP(s, state, x) state || x
#define F(...) BOOST_PP_SEQ_FOLD_LEFT(OP, 0, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

The above converts the variadic arguments into a boost PP sequence, and then folds it by applying OP. state is the intermediate result of every "iteration". It's initial "value" is 0 (so that the resulting expression not depend on it).

And when running F(1, 2, 3) through gcc -E we get:

0 || 1 || 2 || 3

The major caveat with an empty argument list (a GCC extension formally, not valid C99), is that it will expand to 0 ||. So that is worth keeping in mind. Argument counting (with Boost.PP) may possibly be used to choose between one of two implementation depending on 0 or more args to get around the issue.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458